/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
 * http://www.gnu.org/licenses/lgpl-3.0.html
 *
 * $Revision: 11220 $
 * $Id: cbeditor.cpp 11220 2017-10-29 16:54:40Z fuscated $
 * $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-17.xx/src/sdk/cbeditor.cpp $
 */

#include "sdk_precomp.h"

#ifndef CB_PRECOMP
    #include <wx/app.h>
    #include <wx/filedlg.h>
    #include <wx/filename.h>
    #include <wx/menu.h>
    #include <wx/notebook.h>
    #include <wx/wfstream.h>

    #include "cbeditor.h" // class's header file

    #include "cbauibook.h"
    #include "cbplugin.h"
    #include "cbproject.h"
    #include "configmanager.h"
    #include "debuggermanager.h"
    #include "editorcolourset.h"
    #include "editormanager.h"
    #include "globals.h"
    #include "infowindow.h"
    #include "logmanager.h"
    #include "macrosmanager.h" // ReplaceMacros
    #include "manager.h"
    #include "pluginmanager.h"
    #include "projectbuildtarget.h"
    #include "projectfile.h"
    #include "projectmanager.h"
    #include "sdk_events.h"
#endif
#include "cbstyledtextctrl.h"
#include "cbcolourmanager.h"

#include <wx/fontutil.h>
#include <wx/splitter.h>

#include "cbeditorprintout.h"
#include "cbdebugger_interfaces.h"
#include "editor_hooks.h"
#include "encodingdetector.h"
#include "filefilters.h"
#include "projectfileoptionsdlg.h"

const wxString g_EditorModified = _T("*");

#define ERROR_STYLE      wxSCI_MARK_SMALLRECT
#define BOOKMARK_STYLE   wxSCI_MARK_ARROW
#define BREAKPOINT_STYLE wxSCI_MARK_CIRCLE
#define DEBUG_STYLE      wxSCI_MARK_ARROW
#define DEBUG_STYLE_HIGHLIGHT wxSCI_MARK_BACKGROUND

#define BREAKPOINT_OTHER_MARKER    1
#define BREAKPOINT_DISABLED_MARKER 2
#define BREAKPOINT_MARKER          3
#define BOOKMARK_MARKER            4
#define ERROR_MARKER               5
#define DEBUG_MARKER               6
#define DEBUG_MARKER_HIGHLIGHT     7

/*行号边框宽度*/
#define C_LINE_MARGIN      0
/*书签,断点边框宽度*/
#define C_MARKER_MARGIN    1
/*更改栏边框宽度*/
#define C_CHANGEBAR_MARGIN 2
/*折叠边框宽度*/
#define C_FOLDING_MARGIN   3

/*
 * 此结构保存cbEditor类的私有数据.
 * 这是一个避免重新构建整个项目的范例(因为cbEditor是一个基本的依赖项),只需添加一个私有的变量或方法.
 * 结果是我们在cbEditor中声明了一个cbEditorInternalData*私有成员,并在.cpp文件中定义了它(这里).
 * 实际上,这意味着我们现在可以从cbEditorInternalData中添加/删除元素,而无需重新生成项目.
 * cbEditor::m_pData是要在代码中使用的变量.
 * 这是第一次建造,也是最后一次销毁.
 *
 * 所以,如果我们想在cbEditor中添加一个新的私有成员,我们将其添加到这里,
 * 并使用m_pData->例如m_pData->lastPosForCodeCompletion访问它,
 * 当然您可以在这里添加成员函数.
 *
 * cbEditorInternalData还包含指向所有者cbEditor(名为m_pOwner)的指针.
 * 使用m_pOwner结构的成员函数可以访问cbEditor函数(甚至是私有函数——它是一个朋友[友元]).
 *
 * 同样的逻辑应该逐渐地在项目的类中使用.
 */
struct cbEditorInternalData {
    cbEditor *m_pOwner;

    cbEditorInternalData(cbEditor *owner, LoaderBase *fileLoader = nullptr)
            : m_pOwner(owner),
              m_strip_trailing_spaces(true),
              m_ensure_final_line_end(false),
              m_ensure_consistent_line_ends(true),
              m_LastMarginMenuLine(-1),
              m_LastDebugLine(-1),
              m_useByteOrderMark(false),
              m_byteOrderMarkLength(0),
              m_lineNumbersWidth(0),
              m_lineNumbersWidth2(0),
              m_pFileLoader(fileLoader) {
        /*系统编码*/
        m_encoding = wxLocale::GetSystemEncoding();
        /*如果存在文件管理器(此文件管理是内置的项目文件管理器不是插件的文件管理器!)*/
        if (m_pFileLoader) {
#ifdef fileload_measuring
            wxStopWatch sw;
#endif
            /*编码侦测器*/
            EncodingDetector enc(fileLoader);
            if (enc.IsOK()) {
                m_byteOrderMarkLength = enc.GetBOMSizeInBytes();
                m_useByteOrderMark = enc.UsesBOM();
                m_encoding = enc.GetFontEncoding();
            }
            /*文件负载测量?*/
#ifdef fileload_measuring
            Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("通过文件加载器进行编码: %d 微妙"),(int)sw.Time()));
#endif
        }
    }

    ~cbEditorInternalData() {
        if (m_pFileLoader) {
            delete m_pFileLoader;
            m_pFileLoader = nullptr;
        }
    }
    /*仿函数?*/
    /*保存前删除尾随空白*/
    void StripTrailingSpaces() {
        cbStyledTextCtrl *control = m_pOwner->GetControl();
        /*以下代码改编自SciTE源代码*/

        int maxLines = control->GetLineCount();
        for (int line = 0; line < maxLines; line++) {
            int lineStart = control->PositionFromLine(line);
            int lineEnd = control->GetLineEndPosition(line);
            int i = lineEnd - 1;
            wxChar ch = (wxChar) (control->GetCharAt(i));
            if (control->GetLexer() == wxSCI_LEX_DIFF)
                lineStart++;
            while ((i >= lineStart) && ((ch == _T(' ')) || (ch == _T('\t')))) {
                i--;
                ch = (wxChar) (control->GetCharAt(i));
            }
            if (i < (lineEnd - 1)) {
                control->SetTargetStart(i + 1);
                control->SetTargetEnd(lineEnd);
                control->ReplaceTarget(_T(""));
            }
        }
    }

    /*向文件中添加额外的空行*/

    void EnsureFinalLineEnd() {
        cbStyledTextCtrl *control = m_pOwner->GetControl();
        /*以下代码改编自SciTE源代码*/
        int maxLines = control->GetLineCount();
        int enddoc = control->PositionFromLine(maxLines);
        if (maxLines <= 1 || enddoc > control->PositionFromLine(maxLines - 1))
            control->InsertText(enddoc, GetEOLStr(m_pOwner->GetControl()->GetEOLMode()));
    }

    /*确保所有行以相同的下线模式结束*/
    void EnsureConsistentLineEnds() {
        cbStyledTextCtrl *control = m_pOwner->GetControl();
        /*以下代码改编自SciTE源代码*/
        control->ConvertEOLs(control->GetEOLMode());
    }

    /*设置行号列宽度*/
    void SetLineNumberColWidth(bool both = true) {
        ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("editor"));

        if (cfg->ReadBool(_T("/show_line_numbers"), true)) {
            if (m_pOwner->m_pControl2 && both) {
                int pixelWidth = m_pOwner->m_pControl->TextWidth(wxSCI_STYLE_LINENUMBER, _T("9"));
                int pixelWidth2 = m_pOwner->m_pControl2->TextWidth(wxSCI_STYLE_LINENUMBER, _T("9"));

                if (cfg->ReadBool(_T("/margin/dynamic_width"), false)) {
                    int lineNumChars = 1;
                    int lineCount = m_pOwner->m_pControl->GetLineCount();

                    while (lineCount >= 10) {
                        lineCount /= 10;
                        ++lineNumChars;
                    }

                    int lineNumWidth = lineNumChars * pixelWidth + pixelWidth * 0.75;

                    if (lineNumWidth != m_lineNumbersWidth) {
                        m_pOwner->m_pControl->SetMarginWidth(C_LINE_MARGIN, lineNumWidth);
                        m_lineNumbersWidth = lineNumWidth;
                    }

                    lineNumWidth = lineNumChars * pixelWidth2 + pixelWidth2 * 0.75;
                    if (lineNumWidth != m_lineNumbersWidth2) {
                        m_pOwner->m_pControl2->SetMarginWidth(C_LINE_MARGIN, lineNumWidth);
                        m_lineNumbersWidth2 = lineNumWidth;
                    }
                } else {
                    m_pOwner->m_pControl->SetMarginWidth(C_LINE_MARGIN, pixelWidth * 0.75 + cfg->ReadInt(_T("/margin/width_chars"), 6) * pixelWidth);
                    m_pOwner->m_pControl2->SetMarginWidth(C_LINE_MARGIN, pixelWidth * 0.75 + cfg->ReadInt(_T("/margin/width_chars"), 6) * pixelWidth);
                }
            } else {
                cbStyledTextCtrl *control = m_pOwner->GetControl();
                int *pLineNumbersWidth = nullptr;
                if (control == m_pOwner->m_pControl)
                    pLineNumbersWidth = &m_lineNumbersWidth;
                else
                    pLineNumbersWidth = &m_lineNumbersWidth2;

                int pixelWidth = control->TextWidth(wxSCI_STYLE_LINENUMBER, _T("9"));

                if (cfg->ReadBool(_T("/margin/dynamic_width"), false)) {
                    int lineNumChars = 1;
                    int lineCount = control->GetLineCount();

                    while (lineCount >= 10) {
                        lineCount /= 10;
                        ++lineNumChars;
                    }

                    int lineNumWidth = lineNumChars * pixelWidth + pixelWidth * 0.75;

                    if (lineNumWidth != *pLineNumbersWidth) {
                        control->SetMarginWidth(C_LINE_MARGIN, lineNumWidth);
                        *pLineNumbersWidth = lineNumWidth;
                    }
                } else {
                    control->SetMarginWidth(C_LINE_MARGIN, pixelWidth * 0.75 + cfg->ReadInt(_T("/margin/width_chars"), 6) * pixelWidth);
                }
            }
        } else {
            m_pOwner->m_pControl->SetMarginWidth(C_LINE_MARGIN, 0);
            if (m_pOwner->m_pControl2 && both)
                m_pOwner->m_pControl2->SetMarginWidth(C_LINE_MARGIN, 0);
        }
    }

    /*设置折叠列宽度*/
    void SetFoldingColWidth(bool both = true) {
        float pointSize = m_pOwner->m_pControl->StyleGetFont(wxSCI_STYLE_DEFAULT).GetPointSize();
        if (both) {
            int width = 16 * (pointSize + m_pOwner->m_pControl->GetZoom()) / pointSize;
            if (width < 1)
                width = 1;
            m_pOwner->m_pControl->SetMarginWidth(C_FOLDING_MARGIN, width);
            if (m_pOwner->m_pControl2) {
                width = 16 * (pointSize + m_pOwner->m_pControl2->GetZoom()) / pointSize;
                if (width < 1)
                    width = 1;
                m_pOwner->m_pControl2->SetMarginWidth(C_FOLDING_MARGIN, width);
            }
        } else {
            int width = 16 * (pointSize + m_pOwner->GetControl()->GetZoom()) / pointSize;
            if (width < 1)
                width = 1;
            m_pOwner->GetControl()->SetMarginWidth(C_FOLDING_MARGIN, width);
        }
    }

    /*取网址?*/
    wxString GetUrl() {
        cbStyledTextCtrl *control = m_pOwner->GetControl();
        if (!control)
            return wxEmptyString;

        /*正则表达式匹配*/
        wxRegEx reUrl(wxT("***:(((ht|f)tp(s?)\\:\\/\\/)|(www\\.))(([\\w\\-]+(\\.[\\w\\-]+)+)|localhost)(\\/?)([\\w\\-\\.\\?\\,\\'\\/\\\\\\+&amp;%\\$#]*)?([\\d\\w\\.\\/\\%\\+\\-\\=\\&amp;\\?\\:\\\\\\&quot;\\'\\,\\|\\~\\;]*)"));
        wxString url = control->GetSelectedText();

        /*选择了网址?*/
        if (reUrl.Matches(url)) { return reUrl.GetMatch(url); }
        /*还有光标附近有网址吗?*/
        /*找出起始位置*/
        int startPos = control->GetCurrentPos();
        const wxString space = wxT(" \n\r\t{}");
        wxChar curCh = control->GetCharAt(startPos);
        while ((startPos > 0) && (space.Find(curCh) == -1)) {
            startPos--;
            curCh = control->GetCharAt(startPos);
        }

        /*找出终点位置*/
        int endPos = control->GetCurrentPos();
        int maxPos = control->GetLineEndPosition(control->GetLineCount());
        curCh = control->GetCharAt(endPos);
        while ((endPos < maxPos) && (space.Find(curCh) == -1)) {
            endPos++;
            curCh = control->GetCharAt(endPos);
        }

        url = control->GetTextRange(startPos, endPos);
        if ((control->GetLexer() == wxSCI_LEX_CPP)
            && ((control->GetStyleAt(control->GetCurrentPos()) == wxSCI_C_STRING)
                || (control->GetStyleAt(control->GetCurrentPos()) == wxSCI_C_STRINGEOL))) {
            url.Replace(wxT("\\n"), wxT("\n"));
            url.Replace(wxT("\\r"), wxT("\r"));
            url.Replace(wxT("\\t"), wxT("\t"));
        }

        if (reUrl.Matches(url)) {
            wxString match = reUrl.GetMatch(url);
            if ((url.Find(match) + startPos < control->GetCurrentPos())
                && (url.Find(match) + startPos + (int) match.Length() > control->GetCurrentPos())) {
                /*修剪尾部*/
                url = match(0, match.find_last_not_of(wxT(",.")) + 1);
            } else {
                /*不,离光标太远,返回无效(空)*/
                url = wxEmptyString;
            }
        } else {
            /*不,返回无效(空)*/
            url = wxEmptyString;
        }

        return url;
    }

    /*获取行字符串缩进*/
    static wxString GetLineIndentString(int line, cbStyledTextCtrl *stc) {
        int currLine = (line == -1)
                       ? stc->LineFromPosition(stc->GetCurrentPos())
                       : line;
        wxString text = stc->GetLine(currLine);
        unsigned int len = text.Length();
        wxString indent;
        for (unsigned int i = 0; i < len; ++i) {
            if (text[i] == _T(' ') || text[i] == _T('\t')) {
                indent << text[i];
            } else {
                break;
            }
        }
        return indent;
    }

    /*
     * 检测文档中使用的缩进样式.
     * 起点的值来自许多示例文件的试用和错误.
     * 如果这个函数不能确定使用的样式,它将返回-1.
     */
    static int DetectIndentStyle(cbStyledTextCtrl *stc) {
        int lineCount[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
        // lineCount[0] == 带制表符的行数
        // lineCount[1] == 只有空格的行数
        // lineCount[2 ... 8] == 可被该数整除的行数
        // 从文件中间扫描1000行以生成统计信息

        const int maxLine = std::min(stc->GetLineCount(), stc->GetLineCount() / 2 + 500);
        for (int line = std::max(0, stc->GetLineCount() / 2 - 500); line < maxLine; ++line) {
            const wxString &indent = cbEditorInternalData::GetLineIndentString(line, stc);
            if (indent.IsEmpty())
                continue;
            if (indent.Find(wxT('\t')) != wxNOT_FOUND)
                ++lineCount[0];
            else {
                ++lineCount[1];
                for (int i = 2; i < 9; ++i) {
                    if (indent.Length() % i == 0)
                        ++lineCount[i];
                }
            }
        }

        // tabs
        if (lineCount[0] > 0 && lineCount[1] == 0) { return 0; }
        double total = lineCount[0] + lineCount[1];
        // 不确定->使用默认值
        if (total < 10) { return -1; }
        else if (lineCount[0] / total > 0.75) { return 0; }
        else if (lineCount[1] / total < 0.75) { return -1; }

        total = lineCount[1];
        int tabSize = 8;
        for (int i = 2; i < 8; ++i) {
            if (lineCount[i] > lineCount[tabSize]) { tabSize = i; }
        }
        if (lineCount[tabSize] / total < 0.65) { return -1; }

        switch (tabSize) {
            case 2:
                if ((lineCount[2] - lineCount[6]) / total < 0.1) { return 6; }
                if (lineCount[2] > lineCount[4] * 1.8 || lineCount[4] / total < 0.5) { return 2; }
            case 4:
                if (lineCount[4] > lineCount[8] * 1.8 || lineCount[8] / total < 0.5) { return 4; }
            case 8:
                if (lineCount[8] / total < 0.6) { return -1; }
                return 8;

            case 3:
                if (lineCount[3] > lineCount[6] * 1.8 || lineCount[6] / total < 0.5) { return 3; }
                if (lineCount[6] / total < 0.6) { return -1; }
                return 6;

            default:
                if (lineCount[tabSize] / total < 0.7) { return -1; }
                return tabSize;
        }
    }

    // vars
    bool m_strip_trailing_spaces;
    bool m_ensure_final_line_end;
    bool m_ensure_consistent_line_ends;

    int m_LastMarginMenuLine;
    int m_LastDebugLine;

    bool mFoldingLimit;
    int mFoldingLimitLevel;

    wxFontEncoding m_encoding;
    bool m_useByteOrderMark;
    int m_byteOrderMarkLength;

    int m_lineNumbersWidth;
    int m_lineNumbersWidth2;

    LoaderBase *m_pFileLoader;
};

const int idEmptyMenu = wxNewId();
const int idEdit = wxNewId();
const int idUndo = wxNewId();
const int idRedo = wxNewId();
const int idClearHistory = wxNewId();
const int idCut = wxNewId();
const int idCopy = wxNewId();
const int idPaste = wxNewId();
const int idDelete = wxNewId();
const int idUpperCase = wxNewId();
const int idLowerCase = wxNewId();
const int idSelectAll = wxNewId();
const int idSwapHeaderSource = wxNewId();
const int idOpenContainingFolder = wxNewId();
const int idBookmarks = wxNewId();
const int idBookmarksToggle = wxNewId();
const int idBookmarksPrevious = wxNewId();
const int idBookmarksNext = wxNewId();
const int idBookmarksClearAll = wxNewId();
const int idFolding = wxNewId();
const int idFoldingFoldAll = wxNewId();
const int idFoldingUnfoldAll = wxNewId();
const int idFoldingToggleAll = wxNewId();
const int idFoldingFoldCurrent = wxNewId();
const int idFoldingUnfoldCurrent = wxNewId();
const int idFoldingToggleCurrent = wxNewId();
const int idInsert = wxNewId();
const int idSplit = wxNewId();
const int idSplitHorz = wxNewId();
const int idSplitVert = wxNewId();
const int idUnsplit = wxNewId();
const int idProperties = wxNewId();
const int idAddFileToProject = wxNewId();
const int idRemoveFileFromProject = wxNewId();
const int idShowFileInProject = wxNewId();
const int idOpenUrl = wxNewId();

const int idBookmarkAdd = wxNewId();
const int idBookmarkRemove = wxNewId();
const int idBookmarkRemoveAll = wxNewId();

const int idBreakpointAdd = wxNewId();
const int idBreakpointEdit = wxNewId();
const int idBreakpointRemove = wxNewId();
const long idBreakpointEnable = wxNewId();
const long idBreakpointDisable = wxNewId();

BEGIN_EVENT_TABLE(cbEditor, EditorBase)
    EVT_CLOSE(cbEditor::OnClose)
    /*我们有动态事件,查看ConnectEvents()*/

    EVT_MENU(idUndo, cbEditor::OnContextMenuEntry)
    EVT_MENU(idRedo, cbEditor::OnContextMenuEntry)
    EVT_MENU(idClearHistory, cbEditor::OnContextMenuEntry)
    EVT_MENU(idCut, cbEditor::OnContextMenuEntry)
    EVT_MENU(idCopy, cbEditor::OnContextMenuEntry)
    EVT_MENU(idPaste, cbEditor::OnContextMenuEntry)
    EVT_MENU(idDelete, cbEditor::OnContextMenuEntry)
    EVT_MENU(idUpperCase, cbEditor::OnContextMenuEntry)
    EVT_MENU(idLowerCase, cbEditor::OnContextMenuEntry)
    EVT_MENU(idSelectAll, cbEditor::OnContextMenuEntry)
    EVT_MENU(idSwapHeaderSource, cbEditor::OnContextMenuEntry)
    EVT_MENU(idOpenContainingFolder, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarksToggle, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarksPrevious, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarksNext, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarksClearAll, cbEditor::OnContextMenuEntry)
    EVT_MENU(idFoldingFoldAll, cbEditor::OnContextMenuEntry)
    EVT_MENU(idFoldingUnfoldAll, cbEditor::OnContextMenuEntry)
    EVT_MENU(idFoldingToggleAll, cbEditor::OnContextMenuEntry)
    EVT_MENU(idFoldingFoldCurrent, cbEditor::OnContextMenuEntry)
    EVT_MENU(idFoldingUnfoldCurrent, cbEditor::OnContextMenuEntry)
    EVT_MENU(idFoldingToggleCurrent, cbEditor::OnContextMenuEntry)
    EVT_MENU(idProperties, cbEditor::OnContextMenuEntry)
    EVT_MENU(idAddFileToProject, cbEditor::OnContextMenuEntry)
    EVT_MENU(idRemoveFileFromProject, cbEditor::OnContextMenuEntry)
    EVT_MENU(idShowFileInProject, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarkAdd, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarkRemove, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBookmarkRemoveAll, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBreakpointAdd, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBreakpointEdit, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBreakpointRemove, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBreakpointEnable, cbEditor::OnContextMenuEntry)
    EVT_MENU(idBreakpointDisable, cbEditor::OnContextMenuEntry)
    EVT_MENU(idSplitHorz, cbEditor::OnContextMenuEntry)
    EVT_MENU(idSplitVert, cbEditor::OnContextMenuEntry)
    EVT_MENU(idUnsplit, cbEditor::OnContextMenuEntry)
    EVT_MENU(idOpenUrl, cbEditor::OnContextMenuEntry)

    EVT_SCI_ZOOM(-1, cbEditor::OnZoom)
    EVT_SCI_ZOOM(-1, cbEditor::OnZoom)

END_EVENT_TABLE()

/*在打开的文件中计算EOL样式的行数*/
static void CountLineEnds(cbStyledTextCtrl* control, int &linesCR, int &linesLF, int &linesCRLF)
{
    linesCR = 0;
    linesLF = 0;
    linesCRLF = 0;

    int lengthDoc = control->GetLength();
    const int maxLengthDoc = 1000000;
    char chPrev = ' ';
    char chNext = control->GetCharAt(0);
    for (int i = 0; i < lengthDoc; i++)
    {
        char ch = chNext;
        chNext = control->GetCharAt(i + 1);
        if (ch == '\r')
        {
            if (chNext == '\n')
                linesCRLF++;
            else
                linesCR++;
        }
        else if (ch == '\n')
        {
            if (chPrev != '\r')
                linesLF++;
        }
        else if (i > maxLengthDoc){
            /*如果文件包含太多字符,请停止循环*/
            return;
        }
        chPrev = ch;
    }
}

/*检测控件的行结束符模式.如果一个文件混合了EOLs,我们将使用投票逻辑,并给用户一个InfoWindow通知.*/
static int DetectLineEnds(cbStyledTextCtrl* control)
{
    int eolMode;
    wxString eolModeStr;
    /*初始化行结束符结尾符模式取决于操作系统*/
    if (platform::windows)
    {
        eolMode =  wxSCI_EOL_CRLF;
        eolModeStr = _T("\"CR-LF\"");
    }
    else
    {
        eolMode =  wxSCI_EOL_LF;
        eolModeStr = _T("\"LF\"");
    }

    int linesCR;
    int linesLF;
    int linesCRLF;
    /*计算每个行结束符样式的行数*/
    CountLineEnds(control, linesCR, linesLF, linesCRLF);

    /*阈值逻辑:如果文件不包含任何换行符或最大计数相等(例如:linesLF=5,linesCRLF=5,linesCR=0),则我们将使用初始化EOL模式*/
    if ( (linesLF > linesCR) && (linesLF > linesCRLF) )
    {
        eolMode = wxSCI_EOL_LF;
        eolModeStr = _T("\"LF\"");
    }
    else if ( (linesCR > linesLF) && (linesCR > linesCRLF) )
    {
        eolMode = wxSCI_EOL_CR;
        eolModeStr = _T("\"CR\"");
    }
    else if ( (linesCRLF > linesLF) && (linesCRLF > linesCR))
    {
        eolMode = wxSCI_EOL_CRLF;
        eolModeStr = _T("\"CR-LF\"");
    }

    unsigned int delay = 2000;
    if (  ( (linesCR>0) && (linesCRLF>0) )
       || ( (linesLF>0) && (linesCRLF>0) )
       || ( (linesCR>0) && (linesLF>0) ) )
    {
        /*在混合EOL文件中,向用户发出哔哔声和InfoWindow通知.*/
        wxBell();
        InfoWindow::Display(wxT("混合行结束符"), wxT("找到混合行结束符,设置模式 ") + eolModeStr, delay);
    }
    return eolMode;
}

/*cbEditor类构造函数*/
cbEditor::cbEditor(wxWindow* parent, const wxString& filename, EditorColourSet* theme)
    : EditorBase(parent, filename),
    m_pSplitter(nullptr),
    m_pSizer(nullptr),
    m_pControl(nullptr),
    m_pControl2(nullptr),
    m_foldBackup(nullptr),
    m_SplitType(stNoSplit),
    m_Modified(false),
    m_Index(-1),
    m_pProjectFile(nullptr),
    m_pTheme(theme),
    m_lang(HL_AUTO)
{
    DoInitializations(filename);
}

/*cbEditor类构造函数*/
cbEditor::cbEditor(wxWindow* parent, LoaderBase* fileLdr, const wxString& filename, EditorColourSet* theme)
    : EditorBase(parent, filename),
    m_pSplitter(nullptr),
    m_pSizer(nullptr),
    m_pControl(nullptr),
    m_pControl2(nullptr),
    m_foldBackup(nullptr),
    m_SplitType(stNoSplit),
    m_Modified(false),
    m_Index(-1),
    m_pProjectFile(nullptr),
    m_pTheme(theme),
    m_lang(HL_AUTO)
{
    DoInitializations(filename, fileLdr);
}

/*cbEditor析构函数*/
cbEditor::~cbEditor()
{
    SetSizer(nullptr);
    /*被移进EditorBase,通知插件*/

    UpdateProjectFile();
    if (m_pControl)
    {
        if (m_pProjectFile){m_pProjectFile->editorOpen = false;}
        m_pControl->Destroy();
        m_pControl = nullptr;
    }
    DestroySplitView();
    delete m_pData;
}

/*初始化编辑器*/
void cbEditor::DoInitializations(const wxString& filename, LoaderBase* fileLdr)
{
    /*第一件事!如果我们在将来添加更多的构造函数,不要忘记设置这个!*/
    m_pData = new cbEditorInternalData(this);
    m_pData->m_pFileLoader = fileLdr;
    m_IsBuiltinEditor = true;

    if (!filename.IsEmpty())
    {
        InitFilename(filename);
        wxFileName fname(m_Filename);
        NormalizePath(fname, wxEmptyString);
        m_Filename = fname.GetFullPath();
    }
    else
    {
        static int untitledCounter = 1;
        wxString f;
        cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (prj)
            f.Printf(wxT("%sUntitled%d"), prj->GetBasePath().wx_str(), untitledCounter++);
        else
            f.Printf(wxT("Untitled%d"), untitledCounter++);

        InitFilename(f);
    }
    Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("构造: 文件名=%s\n文件名(短格式)=%s"), m_Filename.c_str(), m_Shortname.c_str()));

    /*初始化左控件(未亮显状态)*/
    m_pSizer = new wxBoxSizer(wxVERTICAL);
    m_pControl = CreateEditor();
    m_pSizer->Add(m_pControl, 1, wxEXPAND);
    SetSizer(m_pSizer);

    // 以下两行代码使编辑器在linux中的行为异常:
    // 当调整其他停靠窗口的大小时,编辑器也不会调整大小,并且它们的大小相同...
    // 如果注释下面两行代码会导致其他平台出现问题,只需在周围放置一个"#ifdef __WXGTK__",然后取消注释即可.
    // m_pSizer->Fit(this);
    // m_pSizer->SetSizeHints(this);

    m_pSizer->SetItemMinSize(m_pControl, 32, 32);

    /*默认情况下,我们不显示标记,marginmask在"InternalSetEditorStyleBeforeFileOpen()"和/或插件中显式设置,这些插件使用标记,如browsemarks-plugin*/
    m_pControl->SetMarginMask(C_LINE_MARGIN,      0);
    m_pControl->SetMarginMask(C_MARKER_MARGIN,    0);
    m_pControl->SetMarginMask(C_CHANGEBAR_MARGIN, 0);
    m_pControl->SetMarginMask(C_FOLDING_MARGIN,   0);

    SetEditorStyleBeforeFileOpen();
    m_IsOK = Open();
    SetEditorStyleAfterFileOpen();
    if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/fold_all_on_open"), false))
        FoldAll();

    /*如果!m_IsOK那么它是一个新文件,所以将修改标志设置为 ON*/
    if (!m_IsOK || filename.IsEmpty())
    {
        SetModified(true);
        m_IsOK = false;
    }
    ConnectEvents(m_pControl);
}

/*通知插件*/
void cbEditor::NotifyPlugins(wxEventType type, int intArg, const wxString& strArg, int xArg, int yArg)
{
    /*没有插件管理器!应用程序关闭?*/
    if (!Manager::Get()->GetPluginManager()){return;}
    CodeBlocksEvent event(type);
    event.SetEditor(this);
    event.SetInt(intArg);
    event.SetString(strArg);
    event.SetX(xArg);
    event.SetY(yArg);
    if (m_pProjectFile)
        event.SetProject(m_pProjectFile->GetParentProject());
    //wxPostEvent(Manager::Get()->GetAppWindow(), event);
    Manager::Get()->GetPluginManager()->NotifyPlugins(event);
}

/*销毁拆分窗口*/
void cbEditor::DestroySplitView()
{
    if (m_pControl2)
    {
        m_pControl2->Destroy();
        m_pControl2 = nullptr;
    }
    if (m_pSplitter)
    {
        m_pSplitter->Destroy();
        m_pSplitter = nullptr;
    }
}

/*获取编辑器文本控件*/
cbStyledTextCtrl* cbEditor::GetControl() const
{
    /*返回上次聚焦的控件(左或右)*/
    if (m_pControl2)
    {
        /*每次控件获得焦点时,它都会存储实际的时间戳,时间戳默认为0,因此如果另一个控件具有键盘焦点,则上一次找到焦点窗口时具有焦点的控件的时间戳将不起作用*/
        if (m_pControl2->GetLastFocusTime() > m_pControl->GetLastFocusTime()) { return m_pControl2; }
    }
    return m_pControl;
}

/*返回文本是否被修改*/
bool cbEditor::GetModified() const
{
    return m_Modified || m_pControl->GetModify();
}

/*设置文本已修改*/
void cbEditor::SetModified(bool modified)
{
    if (modified != m_Modified)
    {
        m_Modified = modified;
        if (!m_Modified) { m_pControl->SetSavePoint(); }

        SetEditorTitle(m_Shortname);
        NotifyPlugins(cbEVT_EDITOR_MODIFIED);
        /*视觉(可见)状态*/
        if (m_pProjectFile) { m_pProjectFile->SetFileState(m_pControl->GetReadOnly() ? fvsReadOnly : (m_Modified ? fvsModified : fvsNormal)); }
    }
}

/*设置编辑器标题*/
void cbEditor::SetEditorTitle(const wxString& title)
{
    if (m_Modified)
        SetTitle(g_EditorModified + title);
    else
        SetTitle(title);
}

/*设置项目文件*/
void cbEditor::SetProjectFile(ProjectFile* project_file, bool preserve_modified) {
    /*如果项目文件已经存在,我们以前来过这里*/
    if (m_pProjectFile == project_file) { return; }
    bool wasmodified = false;
    /*保留已修改参数*/
    if (preserve_modified) { wasmodified = GetModified(); }
    m_pProjectFile = project_file;
    if (m_pProjectFile) {
        /*更新我们的文件*/
        m_Filename = UnixFilename(project_file->file.GetFullPath());

        m_pControl->GotoPos(m_pProjectFile->editorPos);
        m_pControl->ScrollToLine(m_pProjectFile->editorTopLine);
        m_pControl->ScrollToColumn(0);
        m_pControl->SetZoom(m_pProjectFile->editorZoom);
        if (m_pProjectFile->editorSplit != (int) stNoSplit) {
            Split((SplitType) m_pProjectFile->editorSplit);
            if (m_pControl2) {
                m_pSplitter->SetSashPosition(m_pProjectFile->editorSplitPos);
                m_pControl2->GotoPos(m_pProjectFile->editorPos_2);
                m_pControl2->ScrollToLine(m_pProjectFile->editorTopLine_2);
                m_pControl2->ScrollToColumn(0);
                m_pControl2->SetZoom(m_pProjectFile->editorZoom_2);
            }
        }

        m_pData->SetLineNumberColWidth();

        if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/show_folds"), true)) {
            for (unsigned int i = 0; i < m_pProjectFile->editorFoldLinesArray.GetCount(); i++) { m_pControl->ToggleFold(m_pProjectFile->editorFoldLinesArray[i]); }
        }

        m_pProjectFile->editorOpen = true;

        if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/tab_text_relative"), true)) { m_Shortname = m_pProjectFile->relativeToCommonTopLevelPath; }
        else { m_Shortname = m_pProjectFile->file.GetFullName(); }
        SetEditorTitle(m_Shortname);

        if (!wxFileExists(m_Filename)) { m_pProjectFile->SetFileState(fvsMissing); }
        else if (!wxFile::Access(m_Filename.c_str(), wxFile::write))
        {
            /*只读*/
            m_pProjectFile->SetFileState(fvsReadOnly);
        }
    }

#if 1
    wxString dbg;
    dbg << wxT("[编辑器] 文件名: ") << GetFilename() << _T('\n');
    dbg << wxT("[编辑器] 文件名(短格式): ") << GetShortName() << _T('\n');
    dbg << wxT("[编辑器] 已修改: ") << GetModified() << _T('\n');
    dbg << wxT("[编辑器] 工程项目: ") << ((m_pProjectFile && m_pProjectFile->GetParentProject()) ? m_pProjectFile->GetParentProject()->GetTitle() : wxT("未知")) << _T('\n');
    dbg << wxT("[编辑器] 工程文件: ") << (m_pProjectFile ? m_pProjectFile->relativeFilename : wxT("未知")) << _T('\n');
    Manager::Get()->GetLogManager()->DebugLog(dbg);
#endif
    if (preserve_modified) { SetModified(wasmodified); }
}

/*更新编辑器项目文件*/
void cbEditor::UpdateProjectFile()
{
    if (m_pControl && m_pProjectFile)
    {
        m_pProjectFile->editorOpen = true;
        m_pProjectFile->editorSplit = m_SplitType;
        m_pProjectFile->editorPos = m_pControl->GetCurrentPos();
        m_pProjectFile->editorTopLine = m_pControl->GetFirstVisibleLine();
        m_pProjectFile->editorZoom = m_pControl->GetZoom();
        m_pProjectFile->editorSplitActive = 1;
        if (m_pControl2)
        {
            m_pProjectFile->editorSplitPos = m_pSplitter->GetSashPosition();
            m_pProjectFile->editorPos_2 = m_pControl2->GetCurrentPos();
            m_pProjectFile->editorTopLine_2 = m_pControl2->GetFirstVisibleLine();
            m_pProjectFile->editorZoom_2 = m_pControl2->GetZoom();
            if (GetControl()==m_pControl2)
                m_pProjectFile->editorSplitActive = 2;
        }

        if (m_pProjectFile->editorFoldLinesArray.GetCount() != 0)
            m_pProjectFile->editorFoldLinesArray.Clear();

        int i = 0;
        while ((i = m_pControl->ContractedFoldNext(i)) != -1)
            m_pProjectFile->editorFoldLinesArray.Add(i++);
    }
}

/*设置编辑器标记器样式*/
void cbEditor::SetMarkerStyle(int marker, int markerType, wxColor fore, wxColor back)
{
    m_pControl->MarkerDefine(marker, markerType);
    m_pControl->MarkerSetForeground(marker, fore);
    m_pControl->MarkerSetBackground(marker, back);

    if (m_pControl2)
    {
        m_pControl2->MarkerDefine(marker, markerType);
        m_pControl2->MarkerSetForeground(marker, fore);
        m_pControl2->MarkerSetBackground(marker, back);
    }
}

/*折叠行下划线*/
void cbEditor::UnderlineFoldedLines(bool underline)
{
    m_pControl->SetFoldFlags(underline ? 16 : 0);
    if (m_pControl2)
        {m_pControl2->SetFoldFlags(underline ? 16 : 0);}
}

/*创建编辑器样式文本控件*/
cbStyledTextCtrl* cbEditor::CreateEditor()
{
    /*避免gtk-critical,因为大小小于-1(可以在wxAuiNotebook/cbAuiNotebook中发生)*/
    wxSize size = m_pControl ? wxDefaultSize : GetSize();
    size.x = std::max(size.x, -1);
    size.y = std::max(size.y, -1);

    cbStyledTextCtrl* control = new cbStyledTextCtrl(this, wxNewId(), wxDefaultPosition, size);
    control->UsePopUp(false);

    /*编辑器配置器*/
    ConfigManager *config = Manager::Get()->GetConfigManager(_T("editor"));
    /*编辑器编码*/
    wxString encodingName = config->Read(_T("/default_encoding"), wxEmptyString);
    m_pData->m_encoding = wxFontMapper::GetEncodingFromName(encodingName);
    if (m_pData->m_encoding == wxFONTENCODING_MAX && encodingName == wxT("default")) { m_pData->m_encoding = wxFont::GetDefaultEncoding(); }

    /*设置编辑器标签*/
    for (int marker = 0; marker <= wxSCI_MARKNUM_LASTUNUSED; ++marker) { control->MarkerDefine(marker, wxSCI_MARK_EMPTY); }

    return control;
}

/*映射编辑器事件(动态绑定事件)*/
void cbEditor::ConnectEvents(cbStyledTextCtrl* stc)
{
    wxWindowID stcID = stc->GetId();
    /*动态事件*/
    /*标记点击*/
    Connect( stcID, wxEVT_SCI_MARGINCLICK,       wxScintillaEventHandler(cbEditor::OnMarginClick)       );
    /*更新编辑器界面*/
    Connect( stcID, wxEVT_SCI_UPDATEUI,          wxScintillaEventHandler(cbEditor::OnEditorUpdateUI)    );
    /*编辑器更改*/
    Connect( stcID, wxEVT_SCI_CHANGE,            wxScintillaEventHandler(cbEditor::OnEditorChange)      );
    /*编辑器字符被增加*/
    Connect( stcID, wxEVT_SCI_CHARADDED,         wxScintillaEventHandler(cbEditor::OnEditorCharAdded)   );

    Connect( stcID, wxEVT_SCI_DWELLSTART,        wxScintillaEventHandler(cbEditor::OnEditorDwellStart)  );
    Connect( stcID, wxEVT_SCI_DWELLEND,          wxScintillaEventHandler(cbEditor::OnEditorDwellEnd)    );
    /*用户列表选择*/
    Connect( stcID, wxEVT_SCI_USERLISTSELECTION, wxScintillaEventHandler(cbEditor::OnUserListSelection) );
    /*编辑器被修改*/
    Connect( stcID, wxEVT_SCI_MODIFIED,          wxScintillaEventHandler(cbEditor::OnEditorModified)    );

    // 现在,将所有其他闪烁事件绑定到一个公共函数,这样编辑器钩子也可以被通知.
    // 如果使用其他函数实现这些事件之一,请执行以下操作:
    // 在这里注释它,在上面的块中"连接"它,并确保从新的处理程序函数调用OnScintillaEvent().
    // 这将确保在需要时调用所有编辑器挂钩.
    //
    //scintilla(闪烁)事件
    int scintilla_events[] =
    {
//        wxEVT_SCI_CHANGE,
        wxEVT_SCI_STYLENEEDED,
//        wxEVT_SCI_CHARADDED,
        wxEVT_SCI_SAVEPOINTREACHED,
        wxEVT_SCI_SAVEPOINTLEFT,
        wxEVT_SCI_ROMODIFYATTEMPT,
        wxEVT_SCI_KEY,
        wxEVT_SCI_DOUBLECLICK,
//        wxEVT_SCI_UPDATEUI,
//        wxEVT_SCI_MODIFIED,
        wxEVT_SCI_MACRORECORD,
//        wxEVT_SCI_MARGINCLICK,
        wxEVT_SCI_NEEDSHOWN,
        wxEVT_SCI_PAINTED,
//        wxEVT_SCI_USERLISTSELECTION,
        wxEVT_SCI_URIDROPPED,
//        wxEVT_SCI_DWELLSTART,
//        wxEVT_SCI_DWELLEND,
        wxEVT_SCI_START_DRAG,
        wxEVT_SCI_FINISHED_DRAG,
        wxEVT_SCI_DRAG_OVER,
        wxEVT_SCI_DO_DROP,
        wxEVT_SCI_ZOOM,
        wxEVT_SCI_HOTSPOT_CLICK,
        wxEVT_SCI_HOTSPOT_DCLICK,
        wxEVT_SCI_CALLTIP_CLICK,
        wxEVT_SCI_AUTOCOMP_SELECTION,
//        wxEVT_SCI_INDICATOR_CLICK,
//        wxEVT_SCI_INDICATOR_RELEASE,
        wxEVT_SCI_AUTOCOMP_CANCELLED,
        wxEVT_SCI_TAB,
        wxEVT_SCI_ESC,
        // to help enumeration of this array
        // 帮助枚举此数组
        -1
    };
    int i = 0;
    while (scintilla_events[i] != -1)
    {
        Connect( stcID, scintilla_events[i], wxScintillaEventHandler(cbEditor::OnScintillaEvent) );
        ++i;
    }
}

/*分割编辑器窗口*/
void cbEditor::Split(cbEditor::SplitType split)
{
    Freeze();
    /*如果需要,请先取消分割窗口*/
    if (m_pSplitter)
    {
        Unsplit();
        Manager::Yield();
    }
    m_SplitType = split;
    if (m_SplitType == stNoSplit)
    {
        Thaw();
        return;
    }

    /*从尺寸调节器上拆下左控制杆*/
    m_pSizer->Detach(m_pControl);

    /*创建拆分器窗口*/
    m_pSplitter = new wxSplitterWindow(this, wxNewId(), wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE);
    m_pSplitter->SetMinimumPaneSize(32);

    /*创建右控件*/
    m_pControl2 = CreateEditor();
    // 更新控件在这里(在文档附加之前)查看并执行,
    // 加快语法突出显示我们在这里不称为"SetEditorStyleAfterFileOpen",
    // 因为它为左控件中已加载的文本调用SetLanguage,
    // 并大大减慢大文件的加载速度.
    //
    InternalSetEditorStyleBeforeFileOpen(m_pControl2);

    /*确保新控件使用指示器的基本设置(可能由插件设置)*/
    for (int i = 0; i < wxSCI_INDIC_MAX; ++i )
    {
        m_pControl2->IndicatorSetStyle(i, m_pControl->IndicatorGetStyle(i));
        m_pControl2->IndicatorSetUnder(i, m_pControl->IndicatorGetUnder(i));
        m_pControl2->IndicatorSetForeground(i, m_pControl->IndicatorGetForeground(i));
    }

    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
    SetFoldingIndicator(mgr->ReadInt(_T("/folding/indicator"), 2));
    UnderlineFoldedLines(mgr->ReadBool(_T("/folding/underline_folded_line"), true));

    if (m_pTheme)
        m_pTheme->Apply(m_lang, m_pControl2);

    /*把它变成左边控件的活拷贝*/
    m_pControl2->SetDocPointer(m_pControl->GetDocPointer());

    /*在wxGTK>2.9上,我们需要在重新分配和重新冻结编辑器之前解冻,否则整个应用程序将保持冻结状态*/
    #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
    Thaw();
    #endif
    /*拆分器下的两个控件的父级*/
    m_pControl->Reparent(m_pSplitter);
    m_pControl2->Reparent(m_pSplitter);
    #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
    Freeze();
    #endif

    /*在sizer中添加拆分器*/
    m_pSizer->SetDimension(0, 0, GetSize().x, GetSize().y);
    m_pSizer->Add(m_pSplitter, 1, wxEXPAND);

    m_pSizer->Layout();

    /*根据需要拆分*/
    switch (m_SplitType)
    {
        case stHorizontal:
            m_pSplitter->SplitHorizontally(m_pControl, m_pControl2, 0);
            break;

        case stVertical:
            m_pSplitter->SplitVertically(m_pControl, m_pControl2, 0);
            break;

        case stNoSplit:
        default:
            break;
    }

    SetEditorStyleAfterFileOpen();

    /*初始缩放与左/上控件相同*/
    m_pControl2->SetZoom(m_pControl->GetZoom());
    /*确保新控件的行号边距正确*/
    m_pControl2->SetMarginWidth(C_LINE_MARGIN, m_pControl->GetMarginWidth(C_LINE_MARGIN));
    ConnectEvents(m_pControl2);
    NotifyPlugins(cbEVT_EDITOR_SPLIT);
    Thaw();
}

/*取消编辑器窗口分割*/
void cbEditor::Unsplit()
{
    m_SplitType = stNoSplit;
    if (!m_pSplitter){return;}
    Freeze();
    /*如果在右控件上请求"取消窗口分割"请先交换左-右*/
    if (GetControl() == m_pControl2)
    {
        cbStyledTextCtrl* tmp = m_pControl;
        m_pControl = m_pControl2;
        m_pControl2 = tmp;
    }

    /*从sizer中取出分离器*/
    m_pSizer->Detach(m_pSplitter);

    /*在wxGTK>2.9上,我们需要在重新分配和重新冻结编辑器之前解冻,否则整个应用程序将保持冻结状态*/
    #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
    Thaw();
    #endif
    /*父级此控件下的左控件*/
    m_pControl->Reparent(this);
    #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
    Freeze();
    #endif

    /*把它放进去*/
    m_pSizer->Add(m_pControl, 1, wxEXPAND);

    /*当右边的拆分器窗口没有被破坏并且左边的窗口被重新分配给cbEditor时通知插件*/
    NotifyPlugins(cbEVT_EDITOR_UNSPLIT);
    /*销毁分割器和右控件*/
    DestroySplitView();
    /*并布局*/
    m_pSizer->Layout();
    /*解冻重画*/
    Thaw();
}

/*静态 获取编辑器颜色配置选项*/
wxColour cbEditor::GetOptionColour(const wxString& option, const wxColour _default)
{
    return Manager::Get()->GetConfigManager(_T("editor"))->ReadColour(option, _default);
}

/*设置编辑器样式*/
void cbEditor::SetEditorStyle()
{
    SetEditorStyleBeforeFileOpen();
    SetEditorStyleAfterFileOpen();
}

/*(内联)覆盖每种语言使用的选项卡*/
inline void OverrideUseTabsPerLanguage(cbStyledTextCtrl *control)
{
    if (!control){return;}
    /*重写Python文件和makefile的use选项卡设置,获取词法解释器*/
    int lexer = control->GetLexer();
    switch (lexer)
    {
        case wxSCI_LEX_PYTHON:
            control->SetUseTabs(false);
            break;
        case wxSCI_LEX_MAKEFILE:
            control->SetUseTabs(true);
            break;
        default:
            break;
    }
}

/*文件打开前设置编辑器样式*/
void cbEditor::SetEditorStyleBeforeFileOpen()
{
    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
    /*基于首选项更新选项卡文本*/
    if (m_pProjectFile)
    {
        if (mgr->ReadBool(_T("/tab_text_relative"), true))
            m_Shortname = m_pProjectFile->relativeToCommonTopLevelPath;
        else
            m_Shortname = m_pProjectFile->file.GetFullName();
        SetEditorTitle(m_Shortname);
    }

    /*折叠属性*/
    m_pData->mFoldingLimit = mgr->ReadBool(_T("/folding/limit"), false);
    m_pData->mFoldingLimitLevel = mgr->ReadInt(_T("/folding/limit_level"), 1);

    /*EOL 属性*/
    m_pData->m_strip_trailing_spaces = mgr->ReadBool(_T("/eol/strip_trailing_spaces"), true);
    m_pData->m_ensure_final_line_end = mgr->ReadBool(_T("/eol/ensure_final_line_end"), true);
    m_pData->m_ensure_consistent_line_ends = mgr->ReadBool(_T("/eol/ensure_consistent_line_ends"), false);

    /*内置对应方法*/
    InternalSetEditorStyleBeforeFileOpen(m_pControl);

    if (m_pControl2) { InternalSetEditorStyleBeforeFileOpen(m_pControl2); }

    SetFoldingIndicator(mgr->ReadInt(_T("/folding/indicator"), 2));

    SetLanguage( HL_AUTO );

    OverrideUseTabsPerLanguage(m_pControl);
    OverrideUseTabsPerLanguage(m_pControl2);
}

/*文件打开后设置编辑器样式*/
void cbEditor::SetEditorStyleAfterFileOpen()
{
    InternalSetEditorStyleAfterFileOpen(m_pControl);
    if (m_pControl2) { InternalSetEditorStyleAfterFileOpen(m_pControl2); }

    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));

    UnderlineFoldedLines(mgr->ReadBool(_T("/folding/underline_folded_line"), true));

    /*行号*/
    m_pData->SetLineNumberColWidth();
}

// public version of InternalSetEditorStyleBeforeFileOpen
// InternalSetEditorStyleBeforeFileOpen的共用版本
/*应用编辑器样式*/
void cbEditor::ApplyStyles(cbStyledTextCtrl* control)
{
    if (!control){return;}

    InternalSetEditorStyleBeforeFileOpen(control);
    InternalSetEditorStyleAfterFileOpen(control);
}

/*编辑器->打开文件之前设置编辑器样式(内置)*/
void cbEditor::InternalSetEditorStyleBeforeFileOpen(cbStyledTextCtrl* control)
{
    if (!control){return;}

    control->Colourise(0, -1);

    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
    /*将默认编辑器字体大小设置为12像素点*/
    wxFont font(12, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);

    wxString fontstring = mgr->Read(_T("/font"), wxEmptyString);

    if (!fontstring.IsEmpty())
    {
        wxNativeFontInfo nfi;
        nfi.FromString(fontstring);
        font.SetNativeFontInfo(nfi);
    }

    control->SetMouseDwellTime(1000);

    int caretStyle = mgr->ReadInt(_T("/caret/style"), wxSCI_CARETSTYLE_LINE);
    control->SetCaretStyle(caretStyle);
    if (caretStyle == wxSCI_CARETSTYLE_LINE)
        control->SetCaretWidth(mgr->ReadInt(_T("/caret/width"), 1));
    else
        control->SetCaretWidth(1);

    ColourManager *colours = Manager::Get()->GetColourManager();

    control->SetCaretForeground(colours->GetColour(wxT("editor_caret")));
    control->SetCaretPeriod(mgr->ReadInt(_T("/caret/period"), 500));
    control->SetCaretLineVisible(mgr->ReadBool(_T("/highlight_caret_line"), false));
    control->SetCaretLineBackground(GetOptionColour(_T("/highlight_caret_line_colour"), wxColour(0xFF, 0xFF, 0x00)));

    control->SetFoldMarginColour(true, colours->GetColour(wxT("editor_margin_chrome")));
    control->SetFoldMarginHiColour(true, colours->GetColour(wxT("editor_margin_chrome_highlight")));

    control->SetWhitespaceForeground(true, colours->GetColour(wxT("editor_whitespace")));

    /*"驼峰风格选定(CamelCase selection)"的设置*/
    if (mgr->ReadBool(_T("/camel_case"), false))
    {
        /*考虑两种驼峰风格:使用CTRL移动光标和使用CTRL+SHIFT选择：*/
        control->CmdKeyAssign(wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL,                   wxSCI_CMD_WORDPARTLEFT);
        control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL,                   wxSCI_CMD_WORDPARTRIGHT);
        control->CmdKeyAssign(wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL|wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDPARTLEFTEXTEND);
        control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL|wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDPARTRIGHTEXTEND);
    }
    else
    {
        /*否则设置默认的"无驼峰风格(none CamelCase)"键行为(另请参见默认闪烁文档)*/
        control->CmdKeyAssign(wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL,                   wxSCI_CMD_WORDLEFT);
        control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL,                   wxSCI_CMD_WORDRIGHT);
        control->CmdKeyAssign(wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL|wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDLEFTEXTEND);
        control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL|wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDRIGHTEXTEND);
    }

    control->SetUseTabs(mgr->ReadBool(_T("/use_tab"), false));
    control->SetIndentationGuides(mgr->ReadBool(_T("/show_indent_guides"), false)?wxSCI_IV_LOOKBOTH:wxSCI_IV_NONE);
    control->SetTabIndents(mgr->ReadBool(_T("/tab_indents"), true));
    control->SetBackSpaceUnIndents(mgr->ReadBool(_T("/backspace_unindents"), true));
    control->SetWrapMode(mgr->ReadBool(_T("/word_wrap"), false));
    if (mgr->ReadBool(_T("/word_wrap_style_home_end"), true))
    {
        // 在自动换行模式中,如果还不存在,则主/结束键转到换行点,否则转到整行的开始/结束.
        // ALT+HOME/END转到整行的开始/结束.
        // 在展开模式下,HOME/END和ALT+HOME/END之间没有区别
        control->CmdKeyAssign(wxSCI_KEY_END,  wxSCI_SCMOD_NORM,                  wxSCI_CMD_LINEENDWRAP);
        control->CmdKeyAssign(wxSCI_KEY_END,  wxSCI_SCMOD_ALT,                   wxSCI_CMD_LINEEND);
        control->CmdKeyAssign(wxSCI_KEY_END,  wxSCI_SCMOD_SHIFT,                 wxSCI_CMD_LINEENDWRAPEXTEND);
        control->CmdKeyAssign(wxSCI_KEY_END,  wxSCI_SCMOD_SHIFT|wxSCI_SCMOD_ALT, wxSCI_CMD_LINEENDEXTEND);

        /*如果用户希望"Home"键将光标设置在行的最开始处*/
        if (mgr->ReadBool(_T("/simplified_home"), false))
        {
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_NORM,wxSCI_CMD_HOMEWRAP);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_ALT,wxSCI_CMD_HOME);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_SHIFT,wxSCI_CMD_HOMEWRAPEXTEND);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_SHIFT|wxSCI_SCMOD_ALT,wxSCI_CMD_HOMEEXTEND);
        }
        else
        {
            /*否则设置默认的"主"键行为*/
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_NORM,wxSCI_CMD_VCHOMEWRAP);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_ALT,wxSCI_CMD_VCHOME);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_SHIFT,wxSCI_CMD_VCHOMEWRAPEXTEND);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_SHIFT|wxSCI_SCMOD_ALT,wxSCI_CMD_VCHOMEEXTEND);
        }
    }
    else
    {
        /*在自动换行模式下,HOME/END键转到整行的开始/结束.ALT+HOME/END转到换行点*/
        control->CmdKeyAssign(wxSCI_KEY_END,  wxSCI_SCMOD_ALT,                   wxSCI_CMD_LINEENDWRAP);
        control->CmdKeyAssign(wxSCI_KEY_END,  wxSCI_SCMOD_SHIFT|wxSCI_SCMOD_ALT, wxSCI_CMD_LINEENDWRAPEXTEND);

        /*如果用户希望"HOME"键将光标设置在行的最开始处*/
        if (mgr->ReadBool(_T("/simplified_home"), false))
        {
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_ALT,wxSCI_CMD_HOMEWRAP);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_SHIFT|wxSCI_SCMOD_ALT,wxSCI_CMD_HOMEWRAPEXTEND);
        }
        else
        {
            /*否则设置默认的"HOME"键行为*/
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_ALT,wxSCI_CMD_VCHOMEWRAP);
            control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_SCMOD_SHIFT|wxSCI_SCMOD_ALT,wxSCI_CMD_VCHOMEWRAPEXTEND);
        }
    }
    control->SetViewEOL(mgr->ReadBool(_T("/show_eol"), false));
    control->SetViewWhiteSpace(mgr->ReadInt(_T("/view_whitespace"), 0));
    /*边线*/
    control->SetEdgeMode(mgr->ReadInt(_T("/gutter/mode"), 0));
    control->SetEdgeColour(Manager::Get()->GetColourManager()->GetColour(wxT("editor_gutter")));
    control->SetEdgeColumn(mgr->ReadInt(_T("/gutter/column"), 80));

    control->StyleSetFont(wxSCI_STYLE_DEFAULT, font);
    control->StyleClearAll();

    control->SetTabWidth(mgr->ReadInt(_T("/tab_size"), 4));

    // 书签,断点等的边距.
    // FIXME:如何显示带有偏移量的标记?
    control->SetMarginWidth(C_MARKER_MARGIN, 16);
    control->SetMarginType(C_MARKER_MARGIN, wxSCI_MARGIN_SYMBOL);
    control->SetMarginSensitive(C_MARKER_MARGIN, mgr->ReadBool(_T("/margin_1_sensitive"), true));

    /*在这里使用"|",否则我们可能会中断使用边距的插件(如browsemarks)*/
    control->SetMarginMask(C_MARKER_MARGIN,
                           control->GetMarginMask(C_MARKER_MARGIN)
                           | (1 << BOOKMARK_MARKER)
                           | (1 << BREAKPOINT_MARKER)
                           | (1 << BREAKPOINT_DISABLED_MARKER)
                           | (1 << BREAKPOINT_OTHER_MARKER)
                           | (1 << DEBUG_MARKER)
                           | (1 << DEBUG_MARKER_HIGHLIGHT)
                           | (1 << ERROR_MARKER) );

    // 1.) 书签标记等...
    control->MarkerDefine(BOOKMARK_MARKER, BOOKMARK_STYLE);
    control->MarkerSetBackground(BOOKMARK_MARKER, wxColour(0xA0, 0xA0, 0xFF));

    // 2.) 断点标记等...
    const wxString &basepath = ConfigManager::GetDataFolder() + wxT("/manager_resources.zip#zip:/images/12x12/");
    bool imageBP = mgr->ReadBool(_T("/margin_1_image_bp"), true);
    if (imageBP)
    {
      wxBitmap iconBP    = cbLoadBitmap(basepath + wxT("breakpoint.png"),          wxBITMAP_TYPE_PNG);
      wxBitmap iconBPDis = cbLoadBitmap(basepath + wxT("breakpoint_disabled.png"), wxBITMAP_TYPE_PNG);
      wxBitmap iconBPOth = cbLoadBitmap(basepath + wxT("breakpoint_other.png"),    wxBITMAP_TYPE_PNG);
      if (iconBP.IsOk() && iconBPDis.IsOk() && iconBPOth.IsOk())
      {
          control->MarkerDefineBitmap(BREAKPOINT_MARKER,          iconBP   );
          control->MarkerDefineBitmap(BREAKPOINT_DISABLED_MARKER, iconBPDis);
          control->MarkerDefineBitmap(BREAKPOINT_OTHER_MARKER,    iconBPOth);
      }
      else{
          // 应用默认标记
          imageBP = false;
      }
    }
    if (!imageBP)
    {
        control->MarkerDefine(BREAKPOINT_MARKER,                 BREAKPOINT_STYLE);
        control->MarkerSetBackground(BREAKPOINT_MARKER,          wxColour(0xFF, 0x00, 0x00));
        control->MarkerDefine(BREAKPOINT_DISABLED_MARKER,        BREAKPOINT_STYLE);
        control->MarkerSetBackground(BREAKPOINT_DISABLED_MARKER, wxColour(0x90, 0x90, 0x90));
        control->MarkerDefine(BREAKPOINT_OTHER_MARKER,           BREAKPOINT_STYLE);
        control->MarkerSetBackground(BREAKPOINT_OTHER_MARKER,    wxColour(0x59, 0x74, 0x8e));
    }
    // 3.) 调试标记(当前调试的线路)等...
    control->MarkerDefine(DEBUG_MARKER, DEBUG_STYLE);
    control->MarkerSetBackground(DEBUG_MARKER, wxColour(0xFF, 0xFF, 0x00));

    control->MarkerDefine(DEBUG_MARKER_HIGHLIGHT, DEBUG_STYLE_HIGHLIGHT);
    control->MarkerSetBackground(DEBUG_MARKER_HIGHLIGHT, control->GetCaretLineBackground());

    // 4.) 错误标记...
    control->MarkerDefine(ERROR_MARKER, ERROR_STYLE);
    control->MarkerSetBackground(ERROR_MARKER, wxColour(0xFF, 0x00, 0x00));

    // 更改栏边距
    if (mgr->ReadBool(_T("/margin/use_changebar"), true))
    {
        control->SetMarginWidth(C_CHANGEBAR_MARGIN, 4);
        control->SetMarginType(C_CHANGEBAR_MARGIN,  wxSCI_MARGIN_SYMBOL);
        // 在这里使用”|“,否则我们可能会中断使用边距的插件(目前没有)
        control->SetMarginMask(C_CHANGEBAR_MARGIN,
                               control->GetMarginMask(C_CHANGEBAR_MARGIN)
                               | (1 << wxSCI_MARKNUM_CHANGEUNSAVED)
                               | (1 << wxSCI_MARKNUM_CHANGESAVED) );

        control->MarkerDefine(wxSCI_MARKNUM_CHANGEUNSAVED, wxSCI_MARK_LEFTRECT);
        control->MarkerSetBackground(wxSCI_MARKNUM_CHANGEUNSAVED, wxColour(0xFF, 0xE6, 0x04));
        control->MarkerDefine(wxSCI_MARKNUM_CHANGESAVED, wxSCI_MARK_LEFTRECT);
        control->MarkerSetBackground(wxSCI_MARKNUM_CHANGESAVED,   wxColour(0x04, 0xFF, 0x50));
    }
    else
    {
        control->SetMarginWidth(C_CHANGEBAR_MARGIN, 0);
    }

    // 注:编辑器对话框(editorconfigurationdlg.cpp)(构造函数)中有重复的行
    control->SetScrollWidthTracking(      mgr->ReadBool(_T("/margin/scroll_width_tracking"), false));
    control->SetMultipleSelection(        mgr->ReadBool(_T("/selection/multi_select"),       false));
    control->SetAdditionalSelectionTyping(mgr->ReadBool(_T("/selection/multi_typing"),       false));

    unsigned virtualSpace = 0;
    if (mgr->ReadBool(_T("/selection/use_rect_vspace"), false))
        virtualSpace |= wxSCI_SCVS_RECTANGULARSELECTION;
    if (mgr->ReadBool(_T("/selection/use_vspace"), false))
        virtualSpace |= wxSCI_SCVS_USERACCESSIBLE;
    // 以防万一(wxSCI_SCVS_NONE != 0)
    if (!virtualSpace)
        virtualSpace = wxSCI_SCVS_NONE;
    control->SetVirtualSpaceOptions(virtualSpace);
}

// static
// 静态
/*编辑器->打开文件之后设置编辑器样式(内置)*/
void cbEditor::InternalSetEditorStyleAfterFileOpen(cbStyledTextCtrl* control)
{
    if (!control){return;}

    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
    // 设定行结束符,回退值:Windows采用CR+LF,其他平台仅采用LF
    int eolMode = mgr->ReadInt(_T("/eol/eolmode"), platform::windows ? wxSCI_EOL_CRLF : wxSCI_EOL_LF);
    // 自动侦测行结束符(EOL)
    if (eolMode == 3)
        eolMode = DetectLineEnds(control);

    control->SetEOLMode(eolMode);

    // 缩进样式已设置
    if (mgr->ReadBool(_T("/detect_indent"), false))
    {
        // 如果自动检测成功,则重写样式
        int indentStyle = cbEditorInternalData::DetectIndentStyle(control);
        if (indentStyle == 0)
            control->SetUseTabs(true);
        else if (indentStyle != -1)
        {
            control->SetUseTabs(false);
            control->SetTabWidth(indentStyle);
        }
    }

    // 将"#if/#else/#endif解释为灰显未激活的代码
    control->SetProperty(_T("lexer.cpp.track.preprocessor"), mgr->ReadBool(_T("/track_preprocessor"), true) ? _T("1") : _T("0"));

    // 代码折叠
    if (mgr->ReadBool(_T("/folding/show_folds"), true))
    {
        control->SetProperty(_T("fold"),              _T("1"));
        control->SetProperty(_T("fold.html"),         mgr->ReadBool(_T("/folding/fold_xml"), true) ? _T("1") : _T("0"));
        control->SetProperty(_T("fold.comment"),      mgr->ReadBool(_T("/folding/fold_comments"), false) ? _T("1") : _T("0"));
        control->SetProperty(_T("fold.compact"),      _T("0"));
        control->SetProperty(_T("fold.preprocessor"), mgr->ReadBool(_T("/folding/fold_preprocessor"), false) ? _T("1") : _T("0"));

        control->SetFoldFlags(16);
        control->SetMarginType(C_FOLDING_MARGIN, wxSCI_MARGIN_SYMBOL);
        control->SetMarginWidth(C_FOLDING_MARGIN, 16);
        // 在这里使用"|",否则我们可能会中断使用边距的插件(目前没有)
        control->SetMarginMask(C_FOLDING_MARGIN,
                                 control->GetMarginMask(C_FOLDING_MARGIN)
                               | (  wxSCI_MASK_FOLDERS
                                  - (  (1 << wxSCI_MARKNUM_CHANGEUNSAVED)
                                     | (1 << wxSCI_MARKNUM_CHANGESAVED))) );
        control->SetMarginSensitive(C_FOLDING_MARGIN, 1);
    }
    else
    {
        control->SetProperty(_T("fold"), _T("0"));
        control->SetMarginWidth(C_FOLDING_MARGIN, 0);
    }
    control->SetProperty(_T("highlight.wxsmith"), mgr->ReadBool(_T("/highlight_wxsmith"), true) ? _T("1") : _T("0"));

    // 行号
    control->SetMarginType(C_LINE_MARGIN, wxSCI_MARGIN_NUMBER);
}

/*编辑器->设置配色方案*/
void cbEditor::SetColourSet(EditorColourSet* theme)
{
    m_pTheme = theme;
    SetLanguage( m_lang );
}

/*编辑器->获取字体编码*/
wxFontEncoding cbEditor::GetEncoding() const
{
    if (!m_pData)
        return wxFONTENCODING_SYSTEM;

    return m_pData->m_encoding;
}

/*获取编码名称*/
wxString cbEditor::GetEncodingName( ) const
{
    return wxFontMapper::GetEncodingName(GetEncoding());
}

/*设置编码*/
void cbEditor::SetEncoding(wxFontEncoding encoding)
{
    if (!m_pData)
        return;

    if (encoding == wxFONTENCODING_SYSTEM)
        encoding = wxLocale::GetSystemEncoding();

    if (encoding == m_pData->m_encoding)
        return;

    m_pData->m_encoding = encoding;
    SetModified(true);
}

/*获取使用BOM单*/
bool cbEditor::GetUseBom() const
{
    if (!m_pData){return false;}
    return m_pData->m_useByteOrderMark;
}

/*设置使用BOM单*/
void cbEditor::SetUseBom( bool bom )
{
    if (!m_pData)
        return;

    if ( bom == GetUseBom() )
        return;

    m_pData->m_useByteOrderMark = bom;
    SetModified(true);
}

/*重载入文件*/
bool cbEditor::Reload(bool detect_encoding)
{
    // 保持当前位置
    const int pos = m_pControl ? m_pControl->GetCurrentPos() : 0;
    const int pos2 = m_pControl2 ? m_pControl2->GetCurrentPos() : 0;

    // 调用打开
    if (!Open(detect_encoding))
    {
        return false;
    }

    // 重新建立边距样式,宽度等
    SetEditorStyleAfterFileOpen();
    // 返回(如果可能)到旧位置
    if (m_pControl)
    {
        m_pControl->GotoPos(pos);
    }
    if (m_pControl2)
    {
        m_pControl2->GotoPos(pos2);
    }
    return true;
}

/*设置文档修改时间为当前时间*/
void cbEditor::Touch()
{
    m_LastModified = wxDateTime::Now();
}

/*编辑器->设置语言语法高亮*/
void cbEditor::SetLanguage(HighlightLanguage lang)
{
    if (m_pTheme)
        m_lang = m_pTheme->Apply(this, lang);
    else
    {
        //自动
        m_lang = HL_AUTO;
    }
}

/*编辑器->打开编辑器*/
bool cbEditor::Open(bool detectEncoding)
{
    if (m_pProjectFile)
    {
        //文件不存在
        if (!wxFileExists(m_Filename)) {
            m_pProjectFile->SetFileState(fvsMissing);
        }
        else if (!wxFile::Access(m_Filename.c_str(), wxFile::write))
        {
            // 文件只读
            m_pProjectFile->SetFileState(fvsReadOnly);
        }
    }

    //无文件返回
    if (!wxFileExists(m_Filename)) { return false; }

    // 打开文件
    SetReadOnly(false);

    m_pControl->ClearAll();
    m_pControl->SetModEventMask(0);

    //如果不存在编辑器数据内部指针返回
    if (!m_pData) { return false; }

    //编辑器内部数据指针没有载入文件指针,调用文件管理器载入文件
    if (!m_pData->m_pFileLoader)
        m_pData->m_pFileLoader = Manager::Get()->GetFileManager()->Load(m_Filename, false);

#ifdef fileload_measuring
    wxStopWatch sw;
#endif
    EncodingDetector enc((wxByte*)m_pData->m_pFileLoader->GetData(), m_pData->m_pFileLoader->GetLength());
    if (detectEncoding)
    {
        m_pData->m_useByteOrderMark    = enc.UsesBOM();
        m_pData->m_byteOrderMarkLength = enc.GetBOMSizeInBytes();
        m_pData->m_encoding            = enc.GetFontEncoding();

        SetEncoding(enc.GetFontEncoding());
        //设置使用BOM单
        SetUseBom(m_pData->m_byteOrderMarkLength > 0);
    }

    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
#ifdef fileload_measuring
    Manager::Get()->GetLogManager()->DebugLog(F(_T("cbEditor::Open() => Encoding detection and conversion took : %d ms"),(int)sw.Time()));
    sw.Start();
#endif

    m_pControl->InsertText(0, enc.GetWxStr());
    m_pControl->EmptyUndoBuffer(mgr->ReadBool(_T("/margin/use_changebar"), true));
    m_pControl->SetModEventMask(wxSCI_MODEVENTMASKALL);

    // 如果适用,将文件标记为只读
    bool read_only = !wxFile::Access(m_Filename.c_str(), wxFile::write);
    SetReadOnly(read_only);

    wxFileName fname(m_Filename);
    m_LastModified = fname.GetModificationTime();

    SetModified(false);
    //通知插件编辑器打开事件
    NotifyPlugins(cbEVT_EDITOR_OPEN);

    if (m_pData->m_pFileLoader)
    {
        delete m_pData->m_pFileLoader;
        m_pData->m_pFileLoader = nullptr;
    }
#ifdef fileload_measuring
    Manager::Get()->GetLogManager()->DebugLog(F(_T("loading into editor needs : %d ms"),(int)sw.Time()));
#endif
    //刷新应用框架的状态条
    return true;
}

/*编辑器->保存文件*/
bool cbEditor::Save()
{
    if (!GetModified()) { return true; }

    // 记住当前列(插入符号和锚点)
    int columnC = m_pControl->GetColumn(m_pControl->GetCurrentPos());
    int columnA = m_pControl->GetColumn(m_pControl->GetAnchor());

    // 此上下文中所有修改的一个撤消操作(为清晰起见添加了斜大括号)
    m_pControl->BeginUndoAction();
    {
        if (m_pData->m_strip_trailing_spaces)
            m_pData->StripTrailingSpaces();
        if (m_pData->m_ensure_consistent_line_ends)
            m_pData->EnsureConsistentLineEnds();
        if (m_pData->m_ensure_final_line_end)
            m_pData->EnsureFinalLineEnd();
    }
    m_pControl->EndUndoAction();

    /*还原虚拟位置(如果由StripTrailingSpaces()更改)*/
    columnC -= m_pControl->GetColumn(m_pControl->GetCurrentPos());
    columnA -= m_pControl->GetColumn(m_pControl->GetAnchor());
    if (columnC > 0)
        m_pControl->SetSelectionNCaretVirtualSpace(0,  columnC);
    if (columnA > 0)
        m_pControl->SetSelectionNAnchorVirtualSpace(0, columnA);

    if (!m_IsOK)
        return SaveAs();

    m_pControl->BeginUndoAction();
    NotifyPlugins(cbEVT_EDITOR_BEFORE_SAVE);
    m_pControl->EndUndoAction();

    if ( !cbSaveToFile(m_Filename, m_pControl->GetText(), GetEncoding(), GetUseBom()) )
    {
        wxString msg;
        msg.Printf(wxT("无法保存文件 %s ..."), GetFilename().wx_str());
        cbMessageBox(msg, wxT("保存文件错误"), wxICON_ERROR);
        // 失败,文件是只读?
        return false;
    }

    wxFileName fname(m_Filename);
    m_LastModified = fname.GetModificationTime();

    m_IsOK = true;

    m_pControl->SetSavePoint();
    SetModified(false);

    NotifyPlugins(cbEVT_EDITOR_SAVE);
    return true;
}

/*编辑器->文件另存为*/
bool cbEditor::SaveAs()
{
    wxFileName fname;
    fname.Assign(m_Filename);
    ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("app"));

    Manager::Get()->GetLogManager()->Log(mgr->GetConfigFolder());
    Manager::Get()->GetLogManager()->Log(mgr->GetExecutableFolder());

    int StoredIndex = 0;
    wxString Filters = FileFilters::GetFilterString();
    wxString Path = fname.GetPath();
    wxString Extension = fname.GetExt();
    wxString Filter;
    if (!Extension.IsEmpty())
    {
        /*使用当前扩展名作为筛选器,选择属于此文件类型的筛选器：*/
        Extension.Prepend(_T("."));
        Filter = FileFilters::GetFilterString(Extension);
    }
    else if (mgr)
    {
        /*文件类型未知.选择上次使用的筛选器:*/
        Filter = mgr->Read(_T("/file_dialogs/save_file_as/filter"), _T("C/C++ files"));
    }
    if (!Filter.IsEmpty())
    {
        /*我们找到了一个过滤器,查找它的索引:*/
        int sep = Filter.find(_T("|"));
        if (sep != wxNOT_FOUND)
            Filter.Truncate(sep);
        if (!Filter.IsEmpty())
            FileFilters::GetFilterIndexFromName(Filters, Filter, StoredIndex);
    }
    if (mgr && Path.IsEmpty())
        Path = mgr->Read(_T("/file_dialogs/save_file_as/directory"), Path);

    wxFileDialog dlg(Manager::Get()->GetAppWindow(), wxT("保存文件"), Path, fname.GetFullName(),wxEmptyString, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

    // 在此处初始化通配符.
    // 如果在wxFileDialog构造函数中执行此操作,它将检测到文件名没有扩展名,并尝试添加扩展名.
    // 它没有我们想要的过滤器索引的正确值,所以它使用第一个值,即Ada文件的ads.
    // 这只发生在wxGTK上.
    dlg.SetWildcard(Filters);
    dlg.SetFilterIndex(StoredIndex);
    PlaceWindow(&dlg);
    if (dlg.ShowModal() != wxID_OK)
    {
        // 取消
        return false;
    }
    m_Filename = dlg.GetPath();
    Manager::Get()->GetLogManager()->Log(m_Filename);
    fname.Assign(m_Filename);
    m_Shortname = fname.GetFullName();
    SetEditorTitle(m_Shortname);

    // 使m_pProjectFile无效,因为如果保留,它将指向旧名称的项目文件,
    // 并导致通过上下文菜单调用的ProjectManager::RemoveFileFromProject崩溃
    SetProjectFile(nullptr);

    Manager::Get()->GetLogManager()->Log(wxString::Format(wxT("文件名=%s\n文件名(短格式)=%s"), m_Filename.c_str(), m_Shortname.c_str()));

    m_IsOK = true;
    SetLanguage( HL_AUTO );
    SetModified(true);
    SetEditorStyleAfterFileOpen();
    OverrideUseTabsPerLanguage(m_pControl);
    OverrideUseTabsPerLanguage(m_pControl2);
    // 存储上次使用的筛选器和目录
    if (mgr)
    {
        int Index = dlg.GetFilterIndex();
        Filter.Empty();
        if (FileFilters::GetFilterNameFromIndex(Filters, Index, Filter))
            mgr->Write(_T("/file_dialogs/save_file_as/filter"), Filter);
        wxString Test = dlg.GetDirectory();
        mgr->Write(_T("/file_dialogs/save_file_as/directory"), dlg.GetDirectory());
    }
    return Save();
}

/*编辑器->保存折叠状态*/
bool cbEditor::SaveFoldState()
{
    bool bRet = false;
    if ((m_foldBackup = CreateEditor()))
    {
        ApplyStyles(m_foldBackup);
        m_foldBackup->SetText(m_pControl->GetText());
        int count = m_pControl->GetLineCount();
        for (int i = 0; i < count; ++i)
            m_foldBackup->SetFoldLevel(i,m_pControl->GetFoldLevel(i));
        bRet = true;
    }
    return bRet;
}

/*编辑器->修正折叠状态*/
bool cbEditor::FixFoldState()
{
    bool bRet = false;
    if (m_foldBackup)
    {
        int backupLength = m_foldBackup->GetLineCount();
        int realLength = m_pControl->GetLineCount();
        // 应该是一样的,但你永远不知道
        if (backupLength == realLength)
        {
            ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
            // 仅当启用折叠时才修复折叠
            if (mgr->ReadBool(_T("/folding/show_folds"), true))
            {
                int count = m_pControl->GetLineCount();
                for (int i = 0; i < count; ++i)
                {
                    int oldFoldLevel = m_foldBackup->GetFoldLevel(i);
                    int newFoldLevel = m_pControl->GetFoldLevel(i);
                    if (oldFoldLevel != newFoldLevel)
                    {
                        if (m_pControl->GetLineVisible(i) == true)
                            m_pControl->SetFoldExpanded(i, true);
                        else
                        {
                            int parent = m_foldBackup->GetFoldParent(i);
                            while(parent != -1)
                            {
                                m_pControl->ToggleFold(parent);
                                parent = m_foldBackup->GetFoldParent(parent);
                            }
                            m_pControl->ShowLines(i, i);
                            parent = m_foldBackup->GetFoldParent(i);
                            while(parent != -1)
                            {
                                m_pControl->ToggleFold(parent);
                                parent = m_foldBackup->GetFoldParent(parent);
                            }
                        }
                    }
                }
            }
            bRet = true;
        }
        m_foldBackup->Destroy();
        m_foldBackup = nullptr;
    }
    return bRet;
}

/*编辑器->代码自动补全(已经弃用),代码自动补全已经使用插件的自动代码补全*/
void cbEditor::AutoComplete()
{
    //Manager::Get()->GetLogManager()->Log(wxT("cbEditor::AutoComplete()已过时.请改用缩写插件中的AutoComplete(cbEditor &ed)."));
}

/*代码编辑器->执行全部折叠动作*/
void cbEditor::DoFoldAll(int fold)
{
    cbAssert(m_pControl);
    if (m_SplitType != stNoSplit) { cbAssert(m_pControl2); }

    cbStyledTextCtrl* ctrl = GetControl();
    // 最重要的部分!
    ctrl->Colourise(0, -1);
    int count = ctrl->GetLineCount();
    for (int i = 0; i <= count; ++i)
        DoFoldLine(i, fold);
}

/*编辑器->执行来自行折叠块*/
void cbEditor::DoFoldBlockFromLine(int line, int fold)
{
    cbAssert(m_pControl);
    if (m_SplitType != stNoSplit)
        cbAssert(m_pControl2);
    cbStyledTextCtrl* ctrl = GetControl();
    // 最重要的部分!
    ctrl->Colourise(0, -1);
    int i, parent, maxLine, level, UnfoldUpto = line;

    parent = ctrl->GetFoldParent(line);
    level = ctrl->GetFoldLevel(parent);

     /*在展开子项之前,下面的代码将检查子项是否隐藏在父项下*/
    if (fold == 0)
    {
        do
        {
            if (!ctrl->GetFoldExpanded(parent))
                UnfoldUpto = parent;
            if (wxSCI_FOLDLEVELBASE == (level & wxSCI_FOLDLEVELNUMBERMASK))
                break;
            parent = ctrl->GetFoldParent(parent);
            level = ctrl->GetFoldLevel(parent);
        }
        while (parent != -1);
    }

    maxLine = ctrl->GetLastChild(line, -1);

    for (i = UnfoldUpto; i <= maxLine; ++i) { DoFoldLine(i, fold); }
}

/*编辑器->执行折叠行*/
bool cbEditor::DoFoldLine(int line, int fold)
{
    cbAssert(m_pControl);
    if (m_SplitType != stNoSplit)
        cbAssert(m_pControl2);
    cbStyledTextCtrl* ctrl = GetControl();
    int level = ctrl->GetFoldLevel(line);

    //  折叠参数是请求的折叠操作类型:0=展开,1=折叠,2=切换折叠.

    // 检查线条是否为页眉(折叠点).
    if (level & wxSCI_FOLDLEVELHEADERFLAG)
    {
        bool IsExpanded = ctrl->GetFoldExpanded(line);

        // 如果在块已经折叠/展开时发出折叠/展开请求,请忽略该请求.
        if (fold == 0 &&  IsExpanded) return true;
        if (fold == 1 && !IsExpanded) return true;

        // 仅当当前块将被折叠(即当前块已展开)时才应用折叠级别限制,当然必须启用折叠级别限制.
        // 展开不会受到影响.
        if (m_pData->mFoldingLimit && IsExpanded)
        {
            if ((level & wxSCI_FOLDLEVELNUMBERMASK) > (wxSCI_FOLDLEVELBASE + m_pData->mFoldingLimitLevel-1))
                return false;
        }

        ctrl->ToggleFold(line);
        return true;
    }
    return false;
}

/*编辑器->全部折叠*/
void cbEditor::FoldAll()
{
    DoFoldAll(1);
}

/*编辑器->全部展开*/
void cbEditor::UnfoldAll()
{
    DoFoldAll(0);
}

/*编辑器->切换全部折叠/展开*/
void cbEditor::ToggleAllFolds()
{
    DoFoldAll(2);
}

/*设置折叠指示器(图标)*/
void cbEditor::SetFoldingIndicator(int id)
{
    // 前景/背景
    wxColor f(0xff, 0xff, 0xff);
    wxColor b(0x80, 0x80, 0x80);
    // 箭头
    if (id == 0)
    {
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN,    wxSCI_MARK_ARROWDOWN,  f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDER,        wxSCI_MARK_ARROW,      f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB,     wxSCI_MARK_BACKGROUND, f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL,    wxSCI_MARK_BACKGROUND, f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND,     wxSCI_MARK_ARROW,      f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_ARROWDOWN,  f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_BACKGROUND, f, b);
    }
    else if (id == 1)
    {
        // 循环
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN,    wxSCI_MARK_CIRCLEMINUS,          f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDER,        wxSCI_MARK_CIRCLEPLUS,           f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB,     wxSCI_MARK_VLINE,                f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL,    wxSCI_MARK_LCORNERCURVE,         f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND,     wxSCI_MARK_CIRCLEPLUSCONNECTED,  f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_CIRCLEMINUSCONNECTED, f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_TCORNER,              f, b);
    }
    // Square
    else if (id == 2)
    {
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN,    wxSCI_MARK_BOXMINUS,          f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDER,        wxSCI_MARK_BOXPLUS,           f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB,     wxSCI_MARK_VLINE,             f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL,    wxSCI_MARK_LCORNER,           f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND,     wxSCI_MARK_BOXPLUSCONNECTED,  f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_BOXMINUSCONNECTED, f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_TCORNER,           f, b);
    }

    else if (id == 3)
    {
        // 单个
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN,    wxSCI_MARK_MINUS,      f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDER,        wxSCI_MARK_PLUS,       f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB,     wxSCI_MARK_BACKGROUND, f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL,    wxSCI_MARK_BACKGROUND, f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND,     wxSCI_MARK_PLUS,       f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_MINUS,      f, b);
        SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_BACKGROUND, f, b);
    }
}

/*来自于行对应块折叠*/
void cbEditor::FoldBlockFromLine(int line)
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    DoFoldBlockFromLine(line, 1);
}

/*来自于行对应块的取消折叠*/
void cbEditor::UnfoldBlockFromLine(int line)
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    DoFoldBlockFromLine(line, 0);
}

/*来自于行的块折叠切换*/
void cbEditor::ToggleFoldBlockFromLine(int line)
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    DoFoldBlockFromLine(line, 2);
}

/*转到行*/
void cbEditor::GotoLine(int line, bool centerOnScreen)
{
    cbStyledTextCtrl* control = GetControl();

    // 确保行没有折叠.
    // 这是在移动到该行之前完成的,因为折叠可能会更改行布局.
    control->EnsureVisible(line);

    // 如果行或下面的行是折叠点,那么它将被展开,
    // 这样当行是函数声明(或者只包含它的左大括号[是的,有时会发生])时,将显示正文.
    DoFoldLine(line,0);
    DoFoldLine(line+1,0);

    if (centerOnScreen)
    {
        int linesOnScreen    = control->LinesOnScreen() >> 1;
        int firstVisibleLine = control->GetFirstVisibleLine();
        if (   (line <  firstVisibleLine)
            || (line > (firstVisibleLine + 2*linesOnScreen)) )
        {
            control->GotoLine(line - linesOnScreen);
            control->GotoLine(line + linesOnScreen);
        }
    }
    control->GotoLine(line);
}

/*转到令牌(标记)位置*/
bool cbEditor::GotoTokenPosition(int line, const wxString& tokenName)
{
    cbStyledTextCtrl* control = GetControl();
    if (line > control->GetLineCount()) { return false; }

    // 屏幕中心功能
    GotoLine(line, true);
    // ...并将焦点设置为此编辑器
    SetFocus();
    // 现在突出显示标记
    const int startPos = control->GetCurrentPos();
    const int endPos   = startPos + control->LineLength(line);
    if (endPos <= startPos) { return false; }

    int tokenPos = control->FindText(startPos, endPos, tokenName,
                                     wxSCI_FIND_WHOLEWORD | wxSCI_FIND_MATCHCASE, nullptr);
    if (tokenPos != wxSCI_INVALID_POSITION)
        control->SetSelectionInt(tokenPos, tokenPos + tokenName.Len());
    else{
        // 后退,把光标指向它
        control->GotoPos(startPos);
    }

    return true;
}

/*切换断点标记*/
void cbEditor::BreakpointMarkerToggle(int line)
{
    int marker = m_pControl->MarkerGet(line);
    if      (marker & (1 << BREAKPOINT_MARKER))
        m_pControl->MarkerDelete(line, BREAKPOINT_MARKER);
    else if (marker & (1 << BREAKPOINT_DISABLED_MARKER))
        m_pControl->MarkerDelete(line, BREAKPOINT_DISABLED_MARKER);
    else
        m_pControl->MarkerAdd(line, BREAKPOINT_MARKER);
}

/*添加断点*/
bool cbEditor::AddBreakpoint(int line, bool notifyDebugger)
{
    if (HasBreakpoint(line))
        return false;

    if (line == -1)
        line = GetControl()->GetCurrentLine();

    if (!notifyDebugger)
    {
        BreakpointMarkerToggle(line);
        return false;
    }

    DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
    if (dbgManager->GetBreakpointDialog()->AddBreakpoint(dbgManager->GetActiveDebugger(), m_Filename, line + 1))
    {
        BreakpointMarkerToggle(line);
        return true;
    }
    return false;
}

/*移除断点*/
bool cbEditor::RemoveBreakpoint(int line, bool notifyDebugger)
{
    if (!HasBreakpoint(line))
        return false;

    if (line == -1)
        line = GetControl()->GetCurrentLine();

    if (!notifyDebugger)
    {
        BreakpointMarkerToggle(line);
        return false;
    }

    DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
    if (dbgManager->GetBreakpointDialog()->RemoveBreakpoint(dbgManager->GetActiveDebugger(), m_Filename, line + 1))
    {
        BreakpointMarkerToggle(line);
        return true;
    }
    return false;
}

/*切换断点*/
void cbEditor::ToggleBreakpoint(int line, bool notifyDebugger)
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    if (!notifyDebugger)
    {
        BreakpointMarkerToggle(line);
        return;
    }

    DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
    cbBreakpointsDlg *dialog = dbgManager->GetBreakpointDialog();
    cbDebuggerPlugin *plugin = dbgManager->GetActiveDebugger();
    if (!plugin || !plugin->SupportsFeature(cbDebuggerFeature::Breakpoints))
        return;

    bool toggle = false;
    if (HasBreakpoint(line))
    {
        if (dialog->RemoveBreakpoint(plugin, m_Filename, line + 1))
            toggle = true;
    }
    else
    {
        if (dialog->AddBreakpoint(plugin, m_Filename, line + 1))
            toggle = true;
    }

    if (toggle)
    {
        BreakpointMarkerToggle(line);
        dialog->Reload();
    }
}

/*代码行存在断点*/
bool cbEditor::HasBreakpoint(int line) const
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    return LineHasMarker(BREAKPOINT_MARKER, line) || LineHasMarker(BREAKPOINT_DISABLED_MARKER, line);
}

/*转至下一个断点*/
void cbEditor::GotoNextBreakpoint()
{
    MarkerNext(BREAKPOINT_MARKER);
}

/*转至前一个断点*/
void cbEditor::GotoPreviousBreakpoint()
{
    MarkerPrevious(BREAKPOINT_MARKER);
}

/*书签切换*/
void cbEditor::ToggleBookmark(int line)
{
    MarkerToggle(BOOKMARK_MARKER, line);
}

/*刷新断点标记*/
void cbEditor::RefreshBreakpointMarkers()
{
    // 首先删除所有断点标记,然后添加活动调试器的标记
    cbStyledTextCtrl *c = GetControl();
    int line = 0;
    while ((line = c->MarkerNext(line, (1 << BREAKPOINT_MARKER))) != -1)
        MarkerToggle(BREAKPOINT_MARKER, line);

    line = 0;
    while ((line = c->MarkerNext(line, (1 << BREAKPOINT_DISABLED_MARKER))) != -1)
        MarkerToggle(BREAKPOINT_DISABLED_MARKER, line);

    line = 0;
    while ((line = c->MarkerNext(line, (1 << BREAKPOINT_OTHER_MARKER))) != -1)
        MarkerToggle(BREAKPOINT_OTHER_MARKER, line);

    const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
    for (DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it)
    {
        const cbDebuggerPlugin *debugger = it->first;
        if (debugger == Manager::Get()->GetDebuggerManager()->GetActiveDebugger())
        {
            for (int ii = 0; ii < debugger->GetBreakpointsCount(); ++ii)
            {
                cb::shared_ptr<const cbBreakpoint> bp = debugger->GetBreakpoint(ii);
                if (bp->GetLocation() == GetFilename())
                {
                    if (bp->IsEnabled())
                        MarkerToggle(BREAKPOINT_MARKER,          bp->GetLine() - 1);
                    else
                        MarkerToggle(BREAKPOINT_DISABLED_MARKER, bp->GetLine() - 1);
                }
            }
        }
        else
        {
            // 非活动调试器的所有断点都使用另一个断点标记
            for (int ii = 0; ii < debugger->GetBreakpointsCount(); ++ii)
            {
                cb::shared_ptr<const cbBreakpoint> bp = debugger->GetBreakpoint(ii);
                if (bp->GetLocation() == GetFilename())
                    MarkerToggle(BREAKPOINT_OTHER_MARKER, bp->GetLine() - 1);
            }
        }
    }
}

/*代码行存在书签*/
bool cbEditor::HasBookmark(int line) const
{
    return LineHasMarker(BOOKMARK_MARKER, line);
}

/*转至下一个书签*/
void cbEditor::GotoNextBookmark()
{
    MarkerNext(BOOKMARK_MARKER);
}

/*转至前一个书签*/
void cbEditor::GotoPreviousBookmark()
{
    MarkerPrevious(BOOKMARK_MARKER);
}

/*清除所有书签*/
void cbEditor::ClearAllBookmarks()
{
    cbStyledTextCtrl* control = GetControl();
    control->MarkerDeleteAll(BOOKMARK_MARKER);
}

/*设置调试行*/
void cbEditor::SetDebugLine(int line)
{
    MarkLine(DEBUG_MARKER, line);
    if (GetControl()->GetCaretLineVisible())
        MarkLine(DEBUG_MARKER_HIGHLIGHT, line);
    m_pData->m_LastDebugLine = line;
}

/*设置错误行*/
void cbEditor::SetErrorLine(int line)
{
    MarkLine(ERROR_MARKER, line);
}

/*撤销操作*/
void cbEditor::Undo()
{
    cbAssert(GetControl());
    GetControl()->Undo();
}

/*重做*/
void cbEditor::Redo()
{
    cbAssert(GetControl());
    GetControl()->Redo();
}

/*清除操作历史*/
void cbEditor::ClearHistory()
{
    cbAssert(GetControl());
    GetControl()->EmptyUndoBuffer(Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/margin/use_changebar"), true));
}

/*转至下一个被改变的行*/
void cbEditor::GotoNextChanged()
{
    cbAssert(GetControl());
    cbStyledTextCtrl* p_Control = GetControl();
    int fromLine = p_Control->LineFromPosition(p_Control->GetCurrentPos());
    int toLine = p_Control->GetLineCount() - 1;
    if (fromLine == toLine)
        fromLine = 0;
    else
        fromLine++;

    int newLine = p_Control->FindChangedLine(fromLine, toLine);
    if (newLine != wxSCI_INVALID_POSITION)
    {
        p_Control->GotoLine(newLine);
        p_Control->MakeNearbyLinesVisible(p_Control->GetCurrentLine());
    }
}

/*转至前一个被改变的行*/
void cbEditor::GotoPreviousChanged()
{
    cbAssert(GetControl());
    cbStyledTextCtrl* p_Control = GetControl();
    int fromLine = p_Control->LineFromPosition(p_Control->GetCurrentPos());
    int toLine = 0;
    if (fromLine == toLine)
        fromLine = p_Control->GetLineCount() - 1;
    else
        fromLine--;

    int newLine = p_Control->FindChangedLine(fromLine, toLine);
    if (newLine != wxSCI_INVALID_POSITION)
    {
        p_Control->GotoLine(newLine);
        p_Control->MakeNearbyLinesVisible(p_Control->GetCurrentLine());
    }
}

/*设置更改集合*/
void cbEditor::SetChangeCollection(bool collectChange)
{
    cbAssert(GetControl());
    GetControl()->SetChangeCollection(collectChange);
}

/*切剪*/
void cbEditor::Cut()
{
    cbAssert(GetControl());
    GetControl()->Cut();
}

/*拷贝*/
void cbEditor::Copy()
{
    cbAssert(GetControl());
    GetControl()->Copy();
}

/*粘帖*/
void cbEditor::Paste()
{
    /*
     * cbAssert(GetControl());
     * GetControl()->Paste();
    */
    /*在GTK3中对于带格式粘帖会产生Gdk-CRITICAL?*/

    LogManager* m_lm=Manager::Get()->GetLogManager();
    m_lm->Log(wxT("26.-->编辑器:开始粘帖"));

    cbAssert(GetControl());

    Freeze();
    cbStyledTextCtrl* ed=GetControl();
    if(!ed){
        Thaw();
        return;
    }
    ed->Paste();
    Thaw();

}

/*可撤销操作*/
bool cbEditor::CanUndo() const
{
    cbAssert(GetControl());
    return !IsReadOnly() && GetControl()->CanUndo();
}

/*可重做操作*/
bool cbEditor::CanRedo() const
{
    cbAssert(GetControl());
    return !IsReadOnly() && GetControl()->CanRedo();
}

/*存在选定文本*/
bool cbEditor::HasSelection() const
{
    cbAssert(GetControl());
    cbStyledTextCtrl* control = GetControl();
    return control->GetSelectionStart() != control->GetSelectionEnd();
}

/*编辑器->可粘帖操作*/
bool cbEditor::CanPaste() const
{
    cbAssert(GetControl());
    if (platform::gtk)
        {return !IsReadOnly();}

    return GetControl()->CanPaste() && !IsReadOnly();
}

/*编辑器是只读*/
bool cbEditor::IsReadOnly() const
{
    cbAssert(GetControl());
    return GetControl()->GetReadOnly();
}

/*编辑器设置只读*/
void cbEditor::SetReadOnly(bool readonly)
{
    cbAssert(GetControl());
    GetControl()->SetReadOnly(readonly);
}

/*代码行存在标记*/
bool cbEditor::LineHasMarker(int marker, int line) const
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    return m_pControl->MarkerGet(line) & (1 << marker);
}

/*切换行标记*/
void cbEditor::MarkerToggle(int marker, int line)
{
    if (line == -1)
        line = GetControl()->GetCurrentLine();
    if (LineHasMarker(marker, line))
        GetControl()->MarkerDelete(line, marker);
    else
        GetControl()->MarkerAdd(line, marker);
}

/*标记下一个*/
void cbEditor::MarkerNext(int marker)
{
    int line = GetControl()->GetCurrentLine() + 1;
    int newLine = GetControl()->MarkerNext(line, 1 << marker);

    // 如果是最后一个标记,则从头开始.
    if (newLine == -1)
    {
        line = 0;
        newLine = GetControl()->MarkerNext(line, 1 << marker);

        if (newLine != -1)
            InfoWindow::Display(wxT("查找书签操作"), wxT("已到达文档结尾"), 1000);
    }

    if (newLine != -1)
        GotoLine(newLine);
}

/*标记前一个*/
void cbEditor::MarkerPrevious(int marker)
{
    int line = GetControl()->GetCurrentLine() - 1;
    int newLine = GetControl()->MarkerPrevious(line, 1 << marker);

    // 回到最后一个如果在第一个标记.
    if (newLine == -1)
    {
        line = GetControl()->GetLineCount();
        newLine = GetControl()->MarkerPrevious(line, 1 << marker);

        if (newLine != -1)
            InfoWindow::Display(wxT("查找书签操作"), wxT("已到达文档结尾"), 1000);
    }

    if (newLine != -1)
        GotoLine(newLine);
}

/*标记代码行*/
void cbEditor::MarkLine(int marker, int line)
{
    if (line == -1)
        GetControl()->MarkerDeleteAll(marker);
    else
        GetControl()->MarkerAdd(line, marker);
}

/*编辑器->转到匹配大括号*/
void cbEditor::GotoMatchingBrace()
{
    cbStyledTextCtrl* control = GetControl();

    // 只有当插入符号在括号之前时,此操作才有效
    int matchingBrace = control->BraceMatch(control->GetCurrentPos());

    // 如果我们还没有找到,我们也会在(pos-1)号位置搜索
    if (matchingBrace == wxSCI_INVALID_POSITION)
        matchingBrace = control->BraceMatch(control->GetCurrentPos() - 1);
    else{
        // 将插入符号保持在括号的同一侧
        ++matchingBrace;
    }

    // 否则查找匹配的预处理器命令
    if (matchingBrace == wxSCI_INVALID_POSITION)
    {
        wxRegEx ppIf(wxT("^[ \t]*#[ \t]*if"));
        wxRegEx ppElse(wxT("^[ \t]*#[ \t]*el"));
        wxRegEx ppEnd(wxT("^[ \t]*#[ \t]*endif"));
        // 获取长度的泛型匹配
        wxRegEx pp(wxT("^[ \t]*#[ \t]*[a-z]*"));
        if (ppIf.Matches(control->GetCurLine()) || ppElse.Matches(control->GetCurLine()))
        {
            // 向前搜索
            int depth = 1;
            for (int i = control->GetCurrentLine() + 1; i < control->GetLineCount(); ++i)
            {
                // 由于性能成本的问题进行限制测试
                if (control->GetLine(i).Find(wxT('#')) != wxNOT_FOUND)
                {
                    // 忽略 else,elseif
                    if (ppIf.Matches(control->GetLine(i)))
                        ++depth;
                    else if (ppEnd.Matches(control->GetLine(i)))
                        --depth;
                }
                if (depth == 0)
                {
                    pp.Matches(control->GetLine(i));
                    matchingBrace = control->PositionFromLine(i) + pp.GetMatch(control->GetLine(i)).Length();
                    break;
                }
            }
        }
        else if (ppEnd.Matches(control->GetCurLine()))
        {
            // search backwards
            // 向后搜索
            int depth = -1;
            for (int i = control->GetCurrentLine() - 1; i >= 0; --i)
            {
                // limit testing due to performance cost
                // 由于性能成本的问题进行限制测试
                if (control->GetLine(i).Find(wxT('#')) != wxNOT_FOUND)
                {
                    // ignore else's, elif's, ...
                    if (ppIf.Matches(control->GetLine(i)))
                        ++depth;
                    else if (ppEnd.Matches(control->GetLine(i)))
                        --depth;
                }
                if (depth == 0)
                {
                    pp.Matches(control->GetLine(i));
                    matchingBrace = control->PositionFromLine(i) + pp.GetMatch(control->GetLine(i)).Length();
                    break;
                }
            }
        }
    }

    // 现在,我们要么找到要么没有
    if (matchingBrace != wxSCI_INVALID_POSITION)
    {
        // 移到实际位置
        control->GotoPos(matchingBrace);
        control->ChooseCaretX();
        // 使附近的行可见
        control->MakeNearbyLinesVisible(control->GetCurrentLine());
    }
}

/*大括号突出显示*/
void cbEditor::HighlightBraces()
{
    cbStyledTextCtrl* control = GetControl();

    ////// 大括号突出显示 ///////
    int currPos = control->GetCurrentPos();
    int newPos = control->BraceMatch(currPos);
    if (newPos == wxSCI_INVALID_POSITION)
    {
        if (currPos > 0)
            currPos--;
        newPos = control->BraceMatch(currPos);
    }
    wxChar ch = control->GetCharAt(currPos);
    if (ch == _T('{') || ch == _T('[') || ch == _T('(') ||
        ch == _T('}') || ch == _T(']') || ch == _T(')'))
    {
        if (newPos != wxSCI_INVALID_POSITION)
        {
            control->BraceHighlight(currPos, newPos);
            const int currColum = control->GetColumn(currPos);
            const int newColum = control->GetColumn(newPos);
            control->SetHighlightGuide((currColum < newColum) ? currColum :newColum);
        }
        else
        {
            control->BraceBadLight(currPos);
        }
    }
    else
    {
        control->BraceHighlight(-1, -1);
    }
}

/*获取行缩进空格数*/
int cbEditor::GetLineIndentInSpaces(int line) const
{
    cbStyledTextCtrl* control = GetControl();
    int currLine = (line == -1)
                    ? control->LineFromPosition(control->GetCurrentPos())
                    : line;
    wxString text = control->GetLine(currLine);
    unsigned int len = text.Length();
    int spaceCount = 0;
    for (unsigned int i = 0; i < len; ++i)
    {
        if (text[i] == _T(' '))
        {
            ++spaceCount;
        }
        else if (text[i] == _T('\t'))
        {
            spaceCount += control->GetTabWidth();
        }
        else
        {
            break;
        }
    }
    return spaceCount;
}

/*获取行缩进字符串*/
wxString cbEditor::GetLineIndentString(int line) const
{
    return cbEditorInternalData::GetLineIndentString(line, GetControl());
}

/*编辑器->根据子菜单的特定Id为上下文菜单创建子菜单(重写基类事件)*/
wxMenu* cbEditor::CreateContextSubMenu(long id)
{
    cbStyledTextCtrl* control = GetControl();
    wxMenu* menu = nullptr;
    if (id == idInsert)
    {
        menu = new wxMenu;
        menu->Append(idEmptyMenu, wxT("空"));
        menu->Enable(idEmptyMenu, false);
    }
    else if (id == idEdit)
    {
        menu = new wxMenu;
        menu->Append(idUndo, wxT("撤消"));
        menu->Append(idRedo, wxT("重做"));
        menu->Append(idClearHistory, wxT("清除更改历史记录"));
        menu->AppendSeparator();
        menu->Append(idCut, wxT("切剪"));
        menu->Append(idCopy, wxT("拷贝"));
        menu->Append(idPaste, wxT("粘贴"));
        menu->Append(idDelete, wxT("删除"));
        menu->AppendSeparator();
        menu->Append(idUpperCase, wxT("文字转大写"));
        menu->Append(idLowerCase, wxT("文字转小写"));
        menu->AppendSeparator();
        menu->Append(idSelectAll, wxT("全选"));

        bool hasSel = control->GetSelectionEnd() - control->GetSelectionStart() != 0;

        menu->Enable(idUndo, control->CanUndo());
        menu->Enable(idRedo, control->CanRedo());
        menu->Enable(idClearHistory, control->CanUndo() || control->CanRedo());
        menu->Enable(idCut, !control->GetReadOnly() && hasSel);
        menu->Enable(idCopy, hasSel);

        // wxGTK错误导致触发意外事件
        if (platform::gtk){
            menu->Enable(idPaste, !control->GetReadOnly());
        }else{
            menu->Enable(idPaste, !control->GetReadOnly() && control->CanPaste());
        }

        menu->Enable(idDelete, !control->GetReadOnly() && hasSel);
        menu->Enable(idUpperCase, !control->GetReadOnly() && hasSel);
        menu->Enable(idLowerCase, !control->GetReadOnly() && hasSel);
    }
    else if (id == idBookmarks)
    {
        menu = new wxMenu;
        menu->Append(idBookmarksToggle, wxT("切换标签"));
        menu->Append(idBookmarksPrevious, wxT("转到前一个标签"));
        menu->Append(idBookmarksNext, wxT("转到下一个标签"));
        menu->Append(idBookmarksClearAll, wxT("清除所有标签"));
    }
    else if (id == idFolding)
    {
        menu = new wxMenu;
        menu->Append(idFoldingFoldAll, wxT("全部折叠"));
        menu->Append(idFoldingUnfoldAll, wxT("全部展开"));
        menu->Append(idFoldingToggleAll, wxT("全部切换"));
        menu->AppendSeparator();
        menu->Append(idFoldingFoldCurrent, wxT("折叠当前块"));
        menu->Append(idFoldingUnfoldCurrent, wxT("展开当前块"));
        menu->Append(idFoldingToggleCurrent, wxT("切换当前块"));
    }
    else
        menu = EditorBase::CreateContextSubMenu(id);

    return menu;
}

/*编辑器->将菜单项添加到上下文菜单(加载插件项之前和之后)*/
void cbEditor::AddToContextMenu(wxMenu* popup,ModuleType type,bool pluginsdone)
{
    bool noeditor = (type != mtEditorManager);
    if (!pluginsdone)
    {
        wxMenu *bookmarks = nullptr, *folding = nullptr, *editsubmenu = nullptr, *insert = nullptr;
        if (!noeditor)
        {
            insert = CreateContextSubMenu(idInsert);
            editsubmenu = CreateContextSubMenu(idEdit);
            bookmarks = CreateContextSubMenu(idBookmarks);
            if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/show_folds"), false))
                folding = CreateContextSubMenu(idFolding);
        }
        if (insert)
        {
            popup->Append(idInsert, wxT("插入"), insert);
            popup->AppendSeparator();
        }
        popup->Append(idSwapHeaderSource, wxT("头文件与实现文件切换"));
        popup->Append(idOpenContainingFolder, wxT("打开包含文件夹"));
        if (!noeditor)
            popup->AppendSeparator();

        if (editsubmenu)
            popup->Append(idEdit, wxT("编辑"), editsubmenu);
        if (bookmarks)
            popup->Append(idBookmarks, wxT("书签"), bookmarks);
        if (folding)
            popup->Append(idFolding, wxT("折叠"), folding);
    }
    else
    {
        if (!noeditor && !m_pData->GetUrl().IsEmpty())
        {
            popup->InsertSeparator(0);
            popup->Insert(0, idOpenUrl, wxT("在浏览器中打开链接"));
        }

        wxMenu* splitMenu = new wxMenu;
        splitMenu->Append(idSplitHorz, wxT("水平(上下)拆分"));
        splitMenu->Append(idSplitVert, wxT("垂直(左右)拆分"));
        splitMenu->AppendSeparator();
        splitMenu->Append(idUnsplit, wxT("取消拆分"));
        // 相应地启用/禁用条目
        bool isSplitHorz = m_pSplitter && m_pSplitter->GetSplitMode() == wxSPLIT_HORIZONTAL;
        bool isSplitVert = m_pSplitter && m_pSplitter->GetSplitMode() == wxSPLIT_VERTICAL;
        splitMenu->Enable(idSplitHorz, !isSplitHorz);
        splitMenu->Enable(idSplitVert, !isSplitVert);
        splitMenu->Enable(idUnsplit, isSplitHorz || isSplitVert);
        popup->Append(idSplit, wxT("拆分视图"), splitMenu);

        popup->Append(idProperties, wxT("属性..."));
        // 项目必须打开
        if (Manager::Get()->GetProjectManager()->GetActiveProject())
        {
            bool isAddRemoveEnabled = true;
            isAddRemoveEnabled = Manager::Get()->GetProjectManager()->GetActiveProject()->GetCurrentlyCompilingTarget() == nullptr;
            popup->AppendSeparator();

            if (m_pProjectFile)
            {
                popup->Append(idRemoveFileFromProject, wxT("从项目中删除文件"));
                popup->Enable(idRemoveFileFromProject, isAddRemoveEnabled);
                popup->Append(idShowFileInProject,     wxT("在项目树中显示文件"));
            }
            else
            {
                popup->Append(idAddFileToProject, wxT("将文件添加到活动项目"));
                popup->Enable(idAddFileToProject, isAddRemoveEnabled);
            }
        }

        // 如果有多个条目,删除"插入和清空(Insert/Empty)"
        wxMenu* insert = nullptr;
        wxMenuItem* insertitem = popup->FindItem(idInsert);
        if (insertitem)
            insert = insertitem->GetSubMenu();
        if (insert)
        {
            if (insert->GetMenuItemCount() > 1)
                insert->Delete(idEmptyMenu);
        }
    }
}

/*响应上下文菜单构建前事件*/
bool cbEditor::OnBeforeBuildContextMenu(const wxPoint& position, ModuleType type)
{
    bool noeditor = (type != mtEditorManager);
    if (!noeditor && position!=wxDefaultPosition)
    {
        // 在编辑器中单击鼠标右键
        // 因为这里的焦点还没有切换(也就是说,左边的控件有焦点,但是用户在右边的控件中单击了右键),所以我们发现活动控件的方式不同...
        wxPoint clientpos(ScreenToClient(position));
        // 数字(如果有) | 断点/书签...如果存在 | 折叠(如果有) | 变更栏(如有)
        const int margin = m_pControl->GetMarginWidth(C_LINE_MARGIN) +
                           m_pControl->GetMarginWidth(C_MARKER_MARGIN) +
                           m_pControl->GetMarginWidth(C_FOLDING_MARGIN) +
                           m_pControl->GetMarginWidth(C_CHANGEBAR_MARGIN);
        wxRect r = m_pControl->GetRect();

        bool inside1 = r.Contains(clientpos);

        cbStyledTextCtrl* control = !m_pControl2 || inside1 ? m_pControl : m_pControl2;
//        control->SetFocus();

        clientpos = control->ScreenToClient(position);
        if (clientpos.x < margin)
        {
            // 保持行
            int pos = control->PositionFromPoint(clientpos);
            m_pData->m_LastMarginMenuLine = control->LineFromPosition(pos);
            // 创建特殊菜单
            wxMenu* popup = new wxMenu;

            cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
            if (plugin && plugin->SupportsFeature(cbDebuggerFeature::Breakpoints))
            {
                bool hasBreak = LineHasMarker(BREAKPOINT_MARKER, m_pData->m_LastMarginMenuLine);
                bool hasBreakDisabled = LineHasMarker(BREAKPOINT_DISABLED_MARKER, m_pData->m_LastMarginMenuLine);

                if (hasBreak || hasBreakDisabled)
                {
                    popup->Append(idBreakpointEdit, wxT("编辑断点"));
                    popup->Append(idBreakpointRemove, wxT("移除断点"));
                    if (hasBreak)
                        popup->Append(idBreakpointDisable, wxT("禁用断点"));
                    if (hasBreakDisabled)
                        popup->Append(idBreakpointEnable, wxT("启用断点"));
                }
                else
                    popup->Append(idBreakpointAdd, wxT("添加断点"));
                popup->AppendSeparator();
            }

            if (LineHasMarker(BOOKMARK_MARKER, m_pData->m_LastMarginMenuLine))
            {
                popup->Append(idBookmarkRemove, wxT("移除书签"));
            }
            else
            {
                popup->Append(idBookmarkAdd, wxT("添加书签"));
            }

            popup->Append(idBookmarkRemoveAll, wxT("移除所有标签"));

            // 显示菜单... wxWindows帮助说不要强制执行该位置
            PopupMenu(popup);

            delete popup;
            return false;
        }

        // 在创建上下文菜单之前,将插入符号移动到鼠标
        // 从鼠标光标获取插入符号位置和行的位置
        const int pos = control->PositionFromPoint(control->ScreenToClient(wxGetMousePosition()));

        // 这将重新启用一次单击"查找声明...",但避免丢失对剪切/复制的选择
        if (control->GetSelectionStart() > pos ||
           control->GetSelectionEnd() < pos)
        {
            control->GotoPos(pos);
        }
    }

    // 遵循默认策略
    return EditorBase::OnBeforeBuildContextMenu(position, type);
}

/*响应上下文菜单构建后事件*/
void cbEditor::OnAfterBuildContextMenu(cb_unused ModuleType type)
{
    // we don't care
    // 我们不在乎
}

/*打印代码*/
void cbEditor::Print(bool selectionOnly, PrintColourMode pcm, bool line_numbers)
{
    cbStyledTextCtrl * control = GetControl();
    if (!control)
        return;

    // 请记住相同的设置,以便我们可以还原它们.
    int oldMarginWidth = control->GetMarginWidth(C_LINE_MARGIN);
    int oldMarginType = control->GetMarginType(C_LINE_MARGIN);
    int oldEdgeMode = control->GetEdgeMode();

    // 打印行号
    control->SetMarginType(C_LINE_MARGIN, wxSCI_MARGIN_NUMBER);
    if (!line_numbers)
    {
        control->SetPrintMagnification(-1);
        control->SetMarginWidth(C_LINE_MARGIN, 0);
    }
    else
    {
        control->SetPrintMagnification(-2);
        control->SetMarginWidth(C_LINE_MARGIN, 1);
    }
    // 不要打印边线行
    control->SetEdgeMode(wxSCI_EDGE_NONE);

    switch (pcm)
    {
        case pcmAsIs:
            control->SetPrintColourMode(wxSCI_PRINT_NORMAL);
            break;
        case pcmBlackAndWhite:
            control->SetPrintColourMode(wxSCI_PRINT_BLACKONWHITE);
            break;
        case pcmColourOnWhite:
            control->SetPrintColourMode(wxSCI_PRINT_COLOURONWHITE);
            break;
        case pcmInvertColours:
            control->SetPrintColourMode(wxSCI_PRINT_INVERTLIGHT);
            break;
        default:
            break;
    }
    InitPrinting();
    cbEditorPrintout printout(m_Filename, control, selectionOnly);
    if (!g_printer->Print(this, &printout, true))
    {
        if (wxPrinter::GetLastError() == wxPRINTER_ERROR)
        {
            cbMessageBox(wxT("打印时出现问题.\n"
                            "可能您当前的打印机设置不正确?"), wxT("打印"), wxICON_ERROR);
            DeInitPrinting();
        }
    }
    else
    {
        wxPrintData* ppd = &(g_printer->GetPrintDialogData().GetPrintData());
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/printerdialog/paperid"), (int)ppd->GetPaperId());
        Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/printerdialog/paperorientation"), (int)ppd->GetOrientation());
    }

    // 还原行号设置
    control->SetMarginType(C_LINE_MARGIN, oldMarginType);
    control->SetMarginWidth(C_LINE_MARGIN, oldMarginWidth);

    // 还原边线设置
    control->SetEdgeMode(oldEdgeMode);

    // 如果需要恢复行号
    m_pData->SetLineNumberColWidth(m_pControl && m_pControl2);
}

// events
// 事件处理函数
/*响应上下文菜单入口点(开始处)*/
void cbEditor::OnContextMenuEntry(wxCommandEvent& event)
{
    cbStyledTextCtrl* control = GetControl();

    // 对于所有弹出菜单项,我们都有一个单一的事件处理程序,
    // 这样我们就可以添加/删除选项,而无需重新编译整个项目(几乎),
    // 但更重要的是,不需要为这样一个微不足道的任务破坏cbEditor的界面...
    const int id = event.GetId();

    if (id == idUndo)
        control->Undo();
    else if (id == idRedo)
        control->Redo();
    else if (id == idClearHistory)
        control->EmptyUndoBuffer(Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/margin/use_changebar"), true));
    else if (id == idCut)
        control->Cut();
    else if (id == idCopy)
        control->Copy();
    else if (id == idPaste)
        control->Paste();
    else if (id == idDelete)
        control->ReplaceSelection(wxEmptyString);
    else if (id == idUpperCase)
        control->UpperCase();
    else if (id == idLowerCase)
        control->LowerCase();
    else if (id == idSelectAll)
        control->SelectAll();
    else if (id == idSwapHeaderSource)
        Manager::Get()->GetEditorManager()->SwapActiveHeaderSource();
    else if (id == idOpenContainingFolder)
        Manager::Get()->GetEditorManager()->OpenContainingFolder();
    else if (id == idBookmarkAdd)
        control->MarkerAdd(m_pData->m_LastMarginMenuLine, BOOKMARK_MARKER);
    else if (id == idBookmarkRemove)
        control->MarkerDelete(m_pData->m_LastMarginMenuLine, BOOKMARK_MARKER);
    else if (id == idBookmarkRemoveAll)
        control->MarkerDeleteAll(BOOKMARK_MARKER);
    else if (id == idBookmarksToggle)
        MarkerToggle(BOOKMARK_MARKER);
    else if (id == idBookmarksNext)
        MarkerNext(BOOKMARK_MARKER);
    else if (id == idBookmarksPrevious)
        MarkerPrevious(BOOKMARK_MARKER);
    else if (id == idBookmarksClearAll)
        control->MarkerDeleteAll(BOOKMARK_MARKER);
    else if (id == idFoldingFoldAll)
        FoldAll();
    else if (id == idFoldingUnfoldAll)
        UnfoldAll();
    else if (id == idFoldingToggleAll)
        ToggleAllFolds();
    else if (id == idFoldingFoldCurrent)
        FoldBlockFromLine();
    else if (id == idFoldingUnfoldCurrent)
        UnfoldBlockFromLine();
    else if (id == idFoldingToggleCurrent)
        ToggleFoldBlockFromLine();
    else if (id == idOpenUrl)
        wxLaunchDefaultBrowser(m_pData->GetUrl());
    else if (id == idSplitHorz)
        Split(stHorizontal);
    else if (id == idSplitVert)
        Split(stVertical);
    else if (id == idUnsplit)
        Unsplit();
    else if (id == idProperties)
    {
        if (m_pProjectFile)
            m_pProjectFile->ShowOptions(this);
        else
        {
            // 活动编辑器不在项目中
            ProjectFileOptionsDlg dlg(this, GetFilename());
            PlaceWindow(&dlg);
            dlg.ShowModal();
        }
    }
    else if (id == idAddFileToProject)
    {
        cbProject *prj = Manager::Get()->GetProjectManager()->GetActiveProject();

        wxArrayInt targets;
        if (Manager::Get()->GetProjectManager()->AddFileToProject(m_Filename, prj, targets) != 0)
        {
            ProjectFile* pf = prj->GetFileByFilename(m_Filename, false);
            SetProjectFile(pf);
            Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
        }
    }
    else if (id == idRemoveFileFromProject)
    {
        if (m_pProjectFile)
        {
            cbProject *prj = m_pProjectFile->GetParentProject();
            Manager::Get()->GetProjectManager()->RemoveFileFromProject(m_pProjectFile, prj);
            Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
        }
    }
    else if (id == idShowFileInProject)
    {
        cbProjectManagerUI& ui = Manager::Get()->GetProjectManager()->GetUI();
        ui.SwitchToProjectsPage();
        ui.ShowFileInTree(*m_pProjectFile);
    }
    else if (id == idBreakpointAdd)
        AddBreakpoint(m_pData->m_LastMarginMenuLine);
    else if (id == idBreakpointEdit)
    {
        cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
        dialog->EditBreakpoint(m_Filename, m_pData->m_LastMarginMenuLine + 1);
    }
    else if (id == idBreakpointRemove)
        RemoveBreakpoint(m_pData->m_LastMarginMenuLine);
    else if (id == idBreakpointEnable)
    {
        cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
        dialog->EnableBreakpoint(m_Filename, m_pData->m_LastMarginMenuLine + 1, true);
    }
    else if (id == idBreakpointDisable)
    {
        cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
        dialog->EnableBreakpoint(m_Filename, m_pData->m_LastMarginMenuLine + 1, false);
    }
    else{event.Skip();}
    //Manager::Get()->GetLogManager()->DebugLog(_T("Leaving OnContextMenuEntry"));
}

/*响应编辑器边距处点击*/
void cbEditor::OnMarginClick(wxScintillaEvent& event)
{
    switch (event.GetMargin())
    {
        case C_MARKER_MARGIN:
        {
            // 书签和断点边距
            int lineYpix = event.GetPosition();
            int line = GetControl()->LineFromPosition(lineYpix);

            ToggleBreakpoint(line);
            break;
        }
        case C_FOLDING_MARGIN:
        {
            // 折叠边距
            int lineYpix = event.GetPosition();
            int line = GetControl()->LineFromPosition(lineYpix);

            GetControl()->ToggleFold(line);
            break;
        }
        default:
            break;
    }
    OnScintillaEvent(event);
}

/*响应编辑器界面更新*/
void cbEditor::OnEditorUpdateUI(wxScintillaEvent& event)
{
    if (Manager::Get()->GetEditorManager()->GetActiveEditor() == this)
    {
        NotifyPlugins(cbEVT_EDITOR_UPDATE_UI);
        // 大括号高亮
        HighlightBraces();
    }
    OnScintillaEvent(event);
}

/*响应编辑器改变事件*/
void cbEditor::OnEditorChange(wxScintillaEvent& event)
{
    SetModified(m_pControl->GetModify());
    OnScintillaEvent(event);
}

/*响应编辑器字符被添加事件*/
void cbEditor::OnEditorCharAdded(wxScintillaEvent& event)
{
    // Manager::Get()->GetLogManager()->Close();
    // 如果消息管理器是自动隐藏的,则在不需要时将其关闭打开

    m_autoIndentDone = false;
    // 此处将调用智能缩进插件
    OnScintillaEvent(event);
    if (!m_autoIndentDone)
    {
        const wxChar ch = event.GetKey();
        cbStyledTextCtrl* control = GetControl();
        // 自动缩进
        if ( (ch == _T('\n')) || ( (control->GetEOLMode() == wxSCI_EOL_CR) && (ch == _T('\r')) ) )
        {
            const int pos = control->GetCurrentPos();
            const int currLine = control->LineFromPosition(pos);
            const bool autoIndent = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/auto_indent"), true);
            if (autoIndent && currLine > 0)
            {
                wxString indent;
                if (control->GetCurLine().Trim().IsEmpty())
                {
                    // 复制最后一个非空行的缩进
                    for (int i = currLine - 1; i >= 0; --i)
                    {
                        const wxString& prevLineStr = control->GetLine(i);
                        if (!(prevLineStr.IsEmpty() || prevLineStr[0] == _T('\n') || prevLineStr[0] == _T('\r')))
                        {
                            indent = GetLineIndentString(i);
                            break;
                        }
                    }
                }
                else
                    indent = GetLineIndentString(currLine - 1);
                if (!indent.IsEmpty())
                {
                    control->BeginUndoAction();

                    control->InsertText(pos, indent);
                    control->GotoPos(pos + indent.Length());
                    control->ChooseCaretX();

                    control->EndUndoAction();
                }
            }
        }

        // 选择大括号补全
        bool braceCompleted = false;
        if (   Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/selection_brace_completion"), false)
            || control->IsBraceShortcutActive() )
        {
            braceCompleted = control->DoSelectionBraceCompletion(ch);
        }

        // 大括号补全
        if (  !braceCompleted
            && Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/brace_completion"), true) )
        {
            control->DoBraceCompletion(ch);
        }
    }
}

/*响应编辑器自动缩进完成事件*/
void cbEditor::AutoIndentDone()
{
    m_autoIndentDone = true;
}

/*响应编辑器开始闭锁事件*/
void cbEditor::OnEditorDwellStart(wxScintillaEvent& event)
{
    if ( !wxTheApp->IsActive() )
        return;

    cbStyledTextCtrl* control = GetControl();
    if (!control)
        return;

    wxRect screenRect = control->GetScreenRect();
    wxPoint ptEvent(event.GetX(), event.GetY());
    ptEvent = control->ClientToScreen(ptEvent);
    wxPoint ptScreen = wxGetMousePosition();
    wxPoint ptClient = control->ScreenToClient(ptScreen);

    double distance = sqrt(  (ptScreen.x - ptEvent.x) * (ptScreen.x - ptEvent.x)
                           + (ptScreen.y - ptEvent.y) * (ptScreen.y - ptEvent.y) );
    if (!screenRect.Contains(ptScreen) || distance > 10)
        return;

    int pos = control->PositionFromPoint(ptClient);
    int style = control->GetStyleAt(pos);
    NotifyPlugins(cbEVT_EDITOR_TOOLTIP, style, wxEmptyString, ptClient.x, ptClient.y);
    wxScintillaEvent newEvent(event);
    newEvent.SetX(ptClient.x);
    newEvent.SetY(ptClient.y);
    OnScintillaEvent(event);
}

/*响应编辑器结束闭锁事件*/
void cbEditor::OnEditorDwellEnd(wxScintillaEvent& event)
{
    NotifyPlugins(cbEVT_EDITOR_TOOLTIP_CANCEL);
    OnScintillaEvent(event);
}

/*响应编辑器被修改事件*/
void cbEditor::OnEditorModified(wxScintillaEvent& event)
{
    //修改信息统计
    // wxString txt = _T("OnEditorModified(): ");
    // int flags = event.GetModificationType();
    // if (flags & wxSCI_MOD_CHANGEMARKER) txt << _T("wxSCI_MOD_CHANGEMARKER, ");
    // if (flags & wxSCI_MOD_INSERTTEXT) txt << _T("wxSCI_MOD_INSERTTEXT, ");
    // if (flags & wxSCI_MOD_DELETETEXT) txt << _T("wxSCI_MOD_DELETETEXT, ");
    // if (flags & wxSCI_MOD_CHANGEFOLD) txt << _T("wxSCI_MOD_CHANGEFOLD, ");
    // if (flags & wxSCI_PERFORMED_USER) txt << _T("wxSCI_PERFORMED_USER, ");
    // if (flags & wxSCI_MOD_BEFOREINSERT) txt << _T("wxSCI_MOD_BEFOREINSERT, ");
    // if (flags & wxSCI_MOD_BEFOREDELETE) txt << _T("wxSCI_MOD_BEFOREDELETE, ");
    // txt << _T("pos=")
    // << wxString::Format(_T("%d"), event.GetPosition())
    // << _T(", line=")
    // << wxString::Format(_T("%d"), event.GetLine())
    // << _T(", linesAdded=")
    // << wxString::Format(_T("%d"), event.GetLinesAdded());
    // Manager::Get()->GetLogManager()->DebugLog(txt);
    //
    // whenever event.GetLinesAdded() != 0, we must re-set breakpoints for lines greater
    // than LineFromPosition(event.GetPosition())
    //
    // 无论何时event.GetLinesAdded()!=0,必须为大于LineFromPosition(event.GetPosition())的行重新设置断点
    //
    int linesAdded = event.GetLinesAdded();
    bool isAdd = event.GetModificationType() & wxSCI_MOD_INSERTTEXT;
    bool isDel = event.GetModificationType() & wxSCI_MOD_DELETETEXT;
    if ((isAdd || isDel) && linesAdded != 0)
    {
        // 是否显示行号现在在SetLineNumberColWidth()中处理
        m_pData->SetLineNumberColWidth();

        // 注意:我不认为每次对每个调试器的轮询都会减慢到让人担心的程度,除非有自动任务定期调用这个例程.
        // 好吧,闪烁事件是经常发生的,尽管我们只有在添加/删除一行代码的情况下才能到达代码的这一部分.
        // 所以.是的,这可能并没有那么糟糕
        int startline = m_pControl->LineFromPosition(event.GetPosition());
        if (m_pControl == event.GetEventObject())
        {
            const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
            cbDebuggerPlugin *active = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
            for (DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it)
            {
                if (it->first != active)
                    it->first->EditorLinesAddedOrRemoved(this, startline + 1, linesAdded);
            }
            if (active)
                active->EditorLinesAddedOrRemoved(this, startline + 1, linesAdded);

            cbBreakpointsDlg *dlg = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
            if (dlg)
                dlg->Reload();
            RefreshBreakpointMarkers();
        }
    }

    // 如果我们从一个折叠的块中移除折叠点(大括号或其他东西),
    // 我们必须使隐藏线可见.
    // 否则,在编辑器关闭并重新打开之前,将无法再访问它们
    if (   (event.GetModificationType() & wxSCI_MOD_CHANGEFOLD)
        && (event.GetFoldLevelPrev() & wxSCI_FOLDLEVELHEADERFLAG) )
    {
        cbStyledTextCtrl* control = GetControl();
        int line = event.GetLine();
        if (! control->GetFoldExpanded(line))
        {
            control->SetFoldExpanded(line, true);
            control->ShowLines(line, control->GetLastChild(line, -1));
        }
    }
    OnScintillaEvent(event);
}


/*响应用户选定文本列表事件*/
void cbEditor::OnUserListSelection(wxScintillaEvent& event)
{
    OnScintillaEvent(event);
}
/*响应编辑器关闭事件*/
void cbEditor::OnClose(cb_unused wxCloseEvent& event)
{
    Manager::Get()->GetEditorManager()->Close(this);
}
/*响应编辑器执行缩进事件*/
void cbEditor::DoIndent()
{
    cbStyledTextCtrl* control = GetControl();
    if (control)
        control->SendMsg(wxSCI_CMD_TAB);
}
/*响应编辑器取消缩进事件*/
void cbEditor::DoUnIndent()
{
    cbStyledTextCtrl* control = GetControl();
    if (control)
        control->SendMsg(wxSCI_CMD_BACKTAB);
}
/*响应编辑器缩放事件*/
void cbEditor::OnZoom(wxScintillaEvent& event)
{
    ConfigManager* mgr =  Manager::Get()->GetConfigManager(_T("editor"));

    int zoom = GetControl()->GetZoom();
    Manager::Get()->GetEditorManager()->SetZoom(zoom);

    // 如果所有编辑器都应该缩放,我们调用cbAuiNotebooks SetZoom()
    bool both = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/zoom_all"));
    if (both)
        Manager::Get()->GetEditorManager()->GetNotebook()->SetZoom(zoom);

    m_pData->SetLineNumberColWidth(both);

    if (mgr->ReadBool(_T("/folding/show_folds"), true))
        m_pData->SetFoldingColWidth(both);

    OnScintillaEvent(event);
}

/*用于为两个控件(如果已拆分)或仅为最后一个活动控件(从cbAuiNotebook调用)设置缩放*/
/*设置缩放*/
void cbEditor::SetZoom(int zoom, bool both)
{
    if (both)
    {
        if (m_pControl->GetZoom() != zoom)
            m_pControl->SetZoom(zoom);
        if (m_pControl2 && (m_pControl2->GetZoom() != zoom))
            m_pControl2->SetZoom(zoom);
    }
    else
    {
        if (GetControl()->GetZoom() != zoom)
            GetControl()->SetZoom(zoom);
    }
}

/*通用闪烁事件处理程序*/
void cbEditor::OnScintillaEvent(wxScintillaEvent& event)
{
//  wxString txt;
//    wxEventType type = event.GetEventType();
//  if (type == wxEVT_SCI_CHANGE) txt << _T("wxEVT_SCI_CHANGE");
//  else if (type == wxEVT_SCI_STYLENEEDED) txt << _T("wxEVT_SCI_STYLENEEDED");
//  else if (type == wxEVT_SCI_CHARADDED) txt << _T("wxEVT_SCI_CHARADDED");
//  else if (type == wxEVT_SCI_SAVEPOINTREACHED) txt << _T("wxEVT_SCI_SAVEPOINTREACHED");
//  else if (type == wxEVT_SCI_SAVEPOINTLEFT) txt << _T("wxEVT_SCI_SAVEPOINTLEFT");
//  else if (type == wxEVT_SCI_ROMODIFYATTEMPT) txt << _T("wxEVT_SCI_ROMODIFYATTEMPT");
//  else if (type == wxEVT_SCI_KEY) txt << _T("wxEVT_SCI_KEY");
//  else if (type == wxEVT_SCI_DOUBLECLICK) txt << _T("wxEVT_SCI_DOUBLECLICK");
//  else if (type == wxEVT_SCI_UPDATEUI) txt << _T("wxEVT_SCI_UPDATEUI");
//  else if (type == wxEVT_SCI_MODIFIED) txt << _T("wxEVT_SCI_MODIFIED");
//  else if (type == wxEVT_SCI_MACRORECORD) txt << _T("wxEVT_SCI_MACRORECORD");
//  else if (type == wxEVT_SCI_MARGINCLICK) txt << _T("wxEVT_SCI_MARGINCLICK");
//  else if (type == wxEVT_SCI_NEEDSHOWN) txt << _T("wxEVT_SCI_NEEDSHOWN");
//  else if (type == wxEVT_SCI_PAINTED) txt << _T("wxEVT_SCI_PAINTED");
//  else if (type == wxEVT_SCI_USERLISTSELECTION) txt << _T("wxEVT_SCI_USERLISTSELECTION");
//  else if (type == wxEVT_SCI_URIDROPPED) txt << _T("wxEVT_SCI_URIDROPPED");
//  else if (type == wxEVT_SCI_DWELLSTART) txt << _T("wxEVT_SCI_DWELLSTART");
//  else if (type == wxEVT_SCI_DWELLEND) txt << _T("wxEVT_SCI_DWELLEND");
//  else if (type == wxEVT_SCI_START_DRAG) txt << _T("wxEVT_SCI_START_DRAG");
//  else if (type == wxEVT_SCI_DRAG_OVER) txt << _T("wxEVT_SCI_DRAG_OVER");
//  else if (type == wxEVT_SCI_DO_DROP) txt << _T("wxEVT_SCI_DO_DROP");
//  else if (type == wxEVT_SCI_ZOOM) txt << _T("wxEVT_SCI_ZOOM");
//  else if (type == wxEVT_SCI_HOTSPOT_CLICK) txt << _T("wxEVT_SCI_HOTSPOT_CLICK");
//  else if (type == wxEVT_SCI_HOTSPOT_DCLICK) txt << _T("wxEVT_SCI_HOTSPOT_DCLICK");
//  else if (type == wxEVT_SCI_CALLTIP_CLICK) txt << _T("wxEVT_SCI_CALLTIP_CLICK");
//  else if (type == wxEVT_SCI_AUTOCOMP_SELECTION) txt << _T("wxEVT_SCI_AUTOCOMP_SELECTION");
//  else if (type == wxEVT_SCI_INDICATOR_CLICK) txt << _T("wxEVT_SCI_INDICATOR_CLICK");
//  else if (type == wxEVT_SCI_INDICATOR_RELEASE) txt << _T("wxEVT_SCI_INDICATOR_RELEASE");
//    Manager::Get()->GetLogManager()->DebugLog(txt);

    // 调用任何钩函子(钩子仿函数)
    if (!ProjectManager::IsBusy() && EditorHooks::HasRegisteredHooks())
    {
        EditorHooks::CallHooks(this, event);
    }
}

/*允许全选*/
bool cbEditor::CanSelectAll() const
{
    int res = 0;
    cbStyledTextCtrl* control = GetControl();
    if (control)
        res = control->GetLength();
    return res > 0;
}

/*全选*/
void cbEditor::SelectAll()
{
    cbStyledTextCtrl* control = GetControl();
    if (control)
        control->SelectAll();
}
